Merge commit '82beb3099fab513a2da769a449a61f0d27e6c3ad' into dev-release
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index 137ac12..2b46e36 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -17,8 +17,6 @@
private static final Set<String> ENUM_WRAP_CONVERT_OWNER =
ImmutableSet.of(
- "j$/nio/file/StandardOpenOption",
- "j$/nio/file/LinkOption",
"j$/nio/file/attribute/PosixFilePermission",
"j$/util/stream/Collector$Characteristics");
private static final Set<String> WRAP_CONVERT_OWNER =
@@ -27,18 +25,14 @@
"j$/nio/file/spi/FileTypeDetector",
"j$/nio/file/Path",
"j$/nio/file/WatchEvent",
- "j$/nio/file/attribute/BasicFileAttributes",
- "j$/nio/file/attribute/BasicFileAttributeView",
- "j$/nio/file/attribute/FileOwnerAttributeView",
- "j$/nio/file/attribute/PosixFileAttributes",
- "j$/nio/file/attribute/PosixFileAttributeView");
+ "j$/nio/file/OpenOption");
static Map<String, String> getJavaWrapConvertOwnerMap() {
- return computeConvertOwnerMap("$VivifiedWrapper");
+ return computeConvertOwnerMap("$Wrapper");
}
static Map<String, String> getJ$WrapConvertOwnerMap() {
- return computeConvertOwnerMap("$Wrapper");
+ return computeConvertOwnerMap("$VivifiedWrapper");
}
private static HashMap<String, String> computeConvertOwnerMap(String suffix) {
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 59ffcc0..1b2e6bf 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.1.6",
+ "version": "1.1.7",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -83,12 +83,14 @@
"java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
},
"custom_conversion": {
- "java.time.ZonedDateTime": "java.time.TimeConversions",
- "java.time.LocalDate": "java.time.TimeConversions",
"java.time.Duration": "java.time.TimeConversions",
- "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
- "java.time.Instant": "java.time.TimeConversions"
+ "java.time.Period": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.ZonedDateTime": "java.time.TimeConversions"
}
},
{
@@ -164,12 +166,14 @@
"java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
},
"custom_conversion": {
- "java.time.ZonedDateTime": "java.time.TimeConversions",
- "java.time.LocalDate": "java.time.TimeConversions",
"java.time.Duration": "java.time.TimeConversions",
- "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
- "java.time.Instant": "java.time.TimeConversions"
+ "java.time.Period": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.ZonedDateTime": "java.time.TimeConversions"
}
},
{
diff --git a/src/library_desugar/java/j$/nio/file/OpenOption.java b/src/library_desugar/java/j$/nio/file/OpenOption.java
index 8b063d1..a22eb7c 100644
--- a/src/library_desugar/java/j$/nio/file/OpenOption.java
+++ b/src/library_desugar/java/j$/nio/file/OpenOption.java
@@ -4,4 +4,12 @@
package j$.nio.file;
-public class OpenOption {}
+public class OpenOption {
+ public static java.nio.file.OpenOption wrap_convert(j$.nio.file.OpenOption option) {
+ return null;
+ }
+
+ public static j$.nio.file.OpenOption wrap_convert(java.nio.file.OpenOption option) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/Path.java b/src/library_desugar/java/j$/nio/file/Path.java
index cb01ec0..781ab5f 100644
--- a/src/library_desugar/java/j$/nio/file/Path.java
+++ b/src/library_desugar/java/j$/nio/file/Path.java
@@ -6,7 +6,7 @@
public class Path {
- public static j$.nio.file.Path inverted_wrap_convert(java.nio.file.Path path) {
+ public static j$.nio.file.Path wrap_convert(java.nio.file.Path path) {
return null;
}
}
diff --git a/src/library_desugar/java/j$/nio/file/StandardOpenOption.java b/src/library_desugar/java/j$/nio/file/StandardOpenOption.java
deleted file mode 100644
index a65da37..0000000
--- a/src/library_desugar/java/j$/nio/file/StandardOpenOption.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 j$.nio.file;
-
-public class StandardOpenOption extends OpenOption {
-
- public static java.nio.file.StandardOpenOption wrap_convert(
- j$.nio.file.StandardOpenOption option) {
- return null;
- }
-
- public static j$.nio.file.StandardOpenOption wrap_convert(
- java.nio.file.StandardOpenOption option) {
- return null;
- }
-}
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 38152b3..8db22b3 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,8 @@
package j$.nio.file.spi;
public class FileSystemProvider {
- public static java.nio.file.spi.FileSystemProvider wrap_convert(FileSystemProvider provider) {
+ public static java.nio.file.spi.FileSystemProvider inverted_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$/time/LocalTime.java b/src/library_desugar/java/j$/time/LocalTime.java
new file mode 100644
index 0000000..3bfd2e2
--- /dev/null
+++ b/src/library_desugar/java/j$/time/LocalTime.java
@@ -0,0 +1,16 @@
+// 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 j$.time;
+
+public class LocalTime {
+
+ public static LocalTime ofNanoOfDay(long toNanoOfDay) {
+ return null;
+ }
+
+ public long toNanoOfDay() {
+ return 0;
+ }
+}
diff --git a/src/library_desugar/java/j$/time/Period.java b/src/library_desugar/java/j$/time/Period.java
new file mode 100644
index 0000000..b7857c1
--- /dev/null
+++ b/src/library_desugar/java/j$/time/Period.java
@@ -0,0 +1,24 @@
+// 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 j$.time;
+
+public class Period {
+
+ public static Period of(int years, int months, int days) {
+ return null;
+ }
+
+ public int getYears() {
+ return 0;
+ }
+
+ public int getMonths() {
+ return 0;
+ }
+
+ public int getDays() {
+ return 0;
+ }
+}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
index d02344d..5be05a8 100644
--- a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
+++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -17,17 +17,19 @@
* runtime environment.
*/
public final class HybridFileSystemProvider {
+
private static final FileSystemProvider INSTANCE = getFileSystemProvider();
private static final FileSystem FILE_SYSTEM_INSTANCE =
INSTANCE.getFileSystem(URI.create("file:///"));
private static FileSystemProvider getFileSystemProvider() {
+ // Note: this fails on non Android devices.
try {
// On API 26 and above, FileSystems is present.
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.wrap_convert(provider);
+ return j$.nio.file.spi.FileSystemProvider.inverted_wrap_convert(provider);
} catch (ClassNotFoundException ignored) {
// We reach this path is API < 26.
}
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
index af2c8f7..da93736 100644
--- a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
+++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -29,7 +29,7 @@
static class PlatformFileTypeDetector extends FileTypeDetector {
@Override
public String probeContentType(Path path) throws IOException {
- return j$.nio.file.Files.probeContentType(j$.nio.file.Path.inverted_wrap_convert(path));
+ return j$.nio.file.Files.probeContentType(j$.nio.file.Path.wrap_convert(path));
}
}
}
diff --git a/src/library_desugar/java/java/nio/file/FileApiFlips.java b/src/library_desugar/java/java/nio/file/FileApiFlips.java
index 71b172a..b68aeb2 100644
--- a/src/library_desugar/java/java/nio/file/FileApiFlips.java
+++ b/src/library_desugar/java/java/nio/file/FileApiFlips.java
@@ -78,7 +78,7 @@
} catch (ClassCastException cce) {
throw exceptionOpenOption(cce);
}
- convertedSet.add(OpenOptionConversions.convert(option));
+ convertedSet.add(j$.nio.file.OpenOption.wrap_convert(option));
}
return convertedSet;
}
@@ -90,7 +90,7 @@
} catch (ClassCastException cce) {
throw exceptionOpenOption(cce);
}
- convertedSet.add(OpenOptionConversions.convert(option));
+ convertedSet.add(j$.nio.file.OpenOption.wrap_convert(option));
}
return convertedSet;
}
diff --git a/src/library_desugar/java/java/nio/file/OpenOptionConversions.java b/src/library_desugar/java/java/nio/file/OpenOptionConversions.java
deleted file mode 100644
index bfe4968..0000000
--- a/src/library_desugar/java/java/nio/file/OpenOptionConversions.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 java.nio.file;
-
-import static java.util.ConversionRuntimeException.exception;
-
-public class OpenOptionConversions {
- public static java.nio.file.OpenOption convert(j$.nio.file.OpenOption option) {
- if (option == null) {
- return null;
- }
- if (option instanceof j$.nio.file.StandardOpenOption) {
- return j$.nio.file.StandardOpenOption.wrap_convert((j$.nio.file.StandardOpenOption) option);
- }
- if (option instanceof j$.nio.file.LinkOption) {
- return j$.nio.file.LinkOption.wrap_convert((j$.nio.file.LinkOption) option);
- }
- throw exception("java.nio.file.OpenOption", option);
- }
-
- public static j$.nio.file.OpenOption convert(java.nio.file.OpenOption option) {
- if (option == null) {
- return null;
- }
- if (option instanceof java.nio.file.StandardOpenOption) {
- return j$.nio.file.StandardOpenOption.wrap_convert((java.nio.file.StandardOpenOption) option);
- }
- if (option instanceof java.nio.file.LinkOption) {
- return j$.nio.file.LinkOption.wrap_convert((java.nio.file.LinkOption) option);
- }
- throw exception("java.nio.file.OpenOption", option);
- }
-}
diff --git a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
index 332c89b..c895d52 100644
--- a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
+++ b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
@@ -4,8 +4,6 @@
package java.nio.file.attribute;
-import static java.util.ConversionRuntimeException.exception;
-
public class FileAttributeConversions {
public static java.nio.file.attribute.FileTime convert(j$.nio.file.attribute.FileTime fileTime) {
@@ -22,67 +20,4 @@
return j$.nio.file.attribute.FileTime.fromMillis(fileTime.toMillis());
}
- public static java.nio.file.attribute.FileAttributeView convert(
- j$.nio.file.attribute.FileAttributeView fileAttributeView) {
- if (fileAttributeView == null) {
- return null;
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.PosixFileAttributeView) {
- return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
- (j$.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.FileOwnerAttributeView) {
- return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
- (j$.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.BasicFileAttributeView) {
- return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
- (j$.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
- }
- throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
- }
-
- public static j$.nio.file.attribute.FileAttributeView convert(
- java.nio.file.attribute.FileAttributeView fileAttributeView) {
- if (fileAttributeView == null) {
- return null;
- }
- if (fileAttributeView instanceof java.nio.file.attribute.PosixFileAttributeView) {
- return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
- (java.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof java.nio.file.attribute.FileOwnerAttributeView) {
- return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
- (java.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof java.nio.file.attribute.BasicFileAttributeView) {
- return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
- (java.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
- }
- throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
- }
-
- public static java.nio.file.attribute.BasicFileAttributes convert(
- j$.nio.file.attribute.BasicFileAttributes fileAttributes) {
- if (fileAttributes == null) {
- return null;
- }
- if (fileAttributes instanceof j$.nio.file.attribute.PosixFileAttributes) {
- return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
- (j$.nio.file.attribute.PosixFileAttributes) fileAttributes);
- }
- return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
- }
-
- public static j$.nio.file.attribute.BasicFileAttributes convert(
- java.nio.file.attribute.BasicFileAttributes fileAttributes) {
- if (fileAttributes == null) {
- return null;
- }
- if (fileAttributes instanceof java.nio.file.attribute.PosixFileAttributes) {
- return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
- (java.nio.file.attribute.PosixFileAttributes) fileAttributes);
- }
- return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
- }
}
diff --git a/src/library_desugar/java/java/time/TimeConversions.java b/src/library_desugar/java/java/time/TimeConversions.java
index 1320546..80824df 100644
--- a/src/library_desugar/java/java/time/TimeConversions.java
+++ b/src/library_desugar/java/java/time/TimeConversions.java
@@ -8,6 +8,34 @@
private TimeConversions() {}
+ public static j$.time.Period convert(java.time.Period period) {
+ if (period == null) {
+ return null;
+ }
+ return j$.time.Period.of(period.getYears(), period.getMonths(), period.getDays());
+ }
+
+ public static java.time.Period convert(j$.time.Period period) {
+ if (period == null) {
+ return null;
+ }
+ return java.time.Period.of(period.getYears(), period.getMonths(), period.getDays());
+ }
+
+ public static j$.time.LocalTime convert(java.time.LocalTime localTime) {
+ if (localTime == null) {
+ return null;
+ }
+ return j$.time.LocalTime.ofNanoOfDay(localTime.toNanoOfDay());
+ }
+
+ public static java.time.LocalTime convert(j$.time.LocalTime localTime) {
+ if (localTime == null) {
+ return null;
+ }
+ return java.time.LocalTime.ofNanoOfDay(localTime.toNanoOfDay());
+ }
+
public static j$.time.ZonedDateTime convert(java.time.ZonedDateTime dateTime) {
if (dateTime == null) {
return null;
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 45d529c..e3caa6e 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -26,7 +26,9 @@
"java.time.Duration": "java.time.TimeConversions",
"java.time.Instant": "java.time.TimeConversions",
"java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Period": "java.time.TimeConversions",
"java.time.ZoneId": "java.time.TimeConversions",
"java.time.ZonedDateTime": "java.time.TimeConversions"
}
@@ -102,6 +104,7 @@
"java.util.PrimitiveIterator$OfDouble",
"java.util.PrimitiveIterator$OfInt",
"java.util.PrimitiveIterator$OfLong",
+ "java.util.Spliterator",
"java.util.Spliterator$OfDouble",
"java.util.Spliterator$OfInt",
"java.util.Spliterator$OfLong",
@@ -114,12 +117,6 @@
"java.util.stream.LongStream",
"java.util.stream.Stream"
],
- "wrapper_conversion_excluding": {
- "java.util.Spliterator": [
- "boolean java.util.Spliterator#hasCharacteristics(int)",
- "long java.util.Spliterator#getExactSizeIfKnown()"
- ]
- },
"custom_conversion": {
"java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
"java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
index 222feda..a4ddd2ba 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -2,7 +2,7 @@
"configuration_format_version": 5,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.2.0",
+ "version": "1.2.1",
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -25,7 +25,9 @@
"java.time.Duration": "java.time.TimeConversions",
"java.time.Instant": "java.time.TimeConversions",
"java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Period": "java.time.TimeConversions",
"java.time.ZoneId": "java.time.TimeConversions",
"java.time.ZonedDateTime": "java.time.TimeConversions"
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index 7b767e1..a00bb68 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -40,7 +40,9 @@
"java.time.Duration": "java.time.TimeConversions",
"java.time.Instant": "java.time.TimeConversions",
"java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Period": "java.time.TimeConversions",
"java.time.ZoneId": "java.time.TimeConversions",
"java.time.ZonedDateTime": "java.time.TimeConversions"
}
@@ -61,6 +63,7 @@
"java.nio.file.": "j$.nio.file."
},
"dont_rewrite_prefix": [
+ "java.nio.channels.AsynchronousChannelGroup",
"java.nio.file.AccessDeniedException",
"java.nio.file.AtomicMoveNotSupportedException",
"java.nio.file.ClosedDirectoryStreamException",
@@ -110,6 +113,7 @@
"java.nio.channels.SeekableByteChannel java.nio.file.spi.FileSystemProvider#newByteChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"],
"java.nio.channels.FileChannel java.nio.file.spi.FileSystemProvider#newFileChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"],
"java.util.List java.nio.file.WatchKey#pollEvents()": [-1, "java.util.List java.nio.file.FileApiFlips#flipWatchEventList(java.util.List)"],
+ "boolean java.nio.file.FileStore#supportsFileAttributeView(java.lang.Class)": [0, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributeView(java.lang.Class)"],
"java.nio.file.attribute.FileAttributeView java.nio.file.spi.FileSystemProvider#getFileAttributeView(java.nio.file.Path, java.lang.Class, java.nio.file.LinkOption[])": [1, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributeView(java.lang.Class)"],
"java.nio.file.attribute.BasicFileAttributes java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.Class, java.nio.file.LinkOption[])": [1, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributes(java.lang.Class)"],
"java.util.Set java.nio.file.attribute.PosixFileAttributes#permissions()": [-1, "java.util.Set java.nio.file.FileApiFlips#flipPosixPermissionSet(java.util.Set)"],
@@ -117,7 +121,6 @@
"java.util.Map java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption[])" : [-1, "java.util.Map java.nio.file.FileApiFlips#flipMapWithMaybeFileTimeValues(java.util.Map)"]
},
"wrapper_conversion": [
- "java.nio.channels.AsynchronousChannel",
"java.nio.channels.CompletionHandler",
"java.nio.file.Path",
"java.nio.file.FileSystem",
@@ -130,8 +133,8 @@
"java.nio.file.WatchEvent$Modifier",
"java.nio.file.attribute.UserPrincipalLookupService",
"java.nio.file.spi.FileSystemProvider",
- "java.nio.file.spi.FileTypeDetector",
"java.nio.file.AccessMode",
+ "java.nio.file.OpenOption",
"java.nio.file.StandardOpenOption",
"java.nio.file.LinkOption",
"java.nio.file.CopyOption",
@@ -145,6 +148,7 @@
"java.nio.file.attribute.PosixFilePermission",
"java.nio.file.attribute.BasicFileAttributes",
"java.nio.file.attribute.PosixFileAttributes",
+ "java.nio.file.attribute.FileAttributeView",
"java.nio.file.attribute.FileOwnerAttributeView",
"java.nio.file.attribute.PosixFileAttributeView",
"java.nio.file.attribute.BasicFileAttributeView"
@@ -157,10 +161,7 @@
]
},
"custom_conversion": {
- "java.nio.file.OpenOption": "java.nio.file.OpenOptionConversions",
- "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions",
- "java.nio.file.attribute.BasicFileAttributes": "java.nio.file.attribute.FileAttributeConversions",
- "java.nio.file.attribute.FileAttributeView": "java.nio.file.attribute.FileAttributeConversions"
+ "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions"
}
},
{
@@ -233,7 +234,7 @@
"java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
- "api_generic_conversion": {
+ "api_generic_types_conversion": {
"java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"]
},
"wrapper_conversion": [
@@ -241,6 +242,7 @@
"java.util.PrimitiveIterator$OfDouble",
"java.util.PrimitiveIterator$OfInt",
"java.util.PrimitiveIterator$OfLong",
+ "java.util.Spliterator",
"java.util.Spliterator$OfDouble",
"java.util.Spliterator$OfInt",
"java.util.Spliterator$OfLong",
@@ -254,10 +256,6 @@
"java.util.stream.Stream"
],
"wrapper_conversion_excluding": {
- "java.util.Spliterator": [
- "boolean java.util.Spliterator#hasCharacteristics(int)",
- "long java.util.Spliterator#getExactSizeIfKnown()"
- ],
"java.nio.channels.FileChannel": [
"long java.nio.channels.FileChannel#read(java.nio.ByteBuffer[])",
"long java.nio.channels.FileChannel#write(java.nio.ByteBuffer[])",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json
index a784fd1..3c503e9 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json
@@ -40,7 +40,9 @@
"java.time.Duration": "java.time.TimeConversions",
"java.time.Instant": "java.time.TimeConversions",
"java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.LocalTime": "java.time.TimeConversions",
"java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Period": "java.time.TimeConversions",
"java.time.ZoneId": "java.time.TimeConversions",
"java.time.ZonedDateTime": "java.time.TimeConversions"
}
@@ -158,14 +160,9 @@
"java.util.function.DoubleToLongFunction",
"java.util.function.LongUnaryOperator",
"java.util.stream.BaseStream",
- "java.util.function.DoublePredicate"
+ "java.util.function.DoublePredicate",
+ "java.util.Spliterator"
],
- "wrapper_conversion_excluding": {
- "java.util.Spliterator": [
- "boolean java.util.Spliterator#hasCharacteristics(int)",
- "long java.util.Spliterator#getExactSizeIfKnown()"
- ]
- },
"custom_conversion": {
"java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
"java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 3887ee7..03ec1dd 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -400,6 +401,9 @@
() -> new KotlinMetadataRewriter(appView).runForD8(executorService));
timing.time(
+ "Startup instrumentation", () -> StartupInstrumentation.run(appView, executorService));
+
+ timing.time(
"Api reference stubber", () -> new ApiReferenceStubber(appView).run(executorService));
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b781d3a..5855643 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -444,8 +443,6 @@
new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
- new StartupInstrumentation(appView).instrumentAllClasses(executorService);
-
assert verifyNoJarApplicationReaders(appView.appInfo().classes());
assert appView.checkForTesting(() -> allReferencesAssignedApiLevel(appViewWithLiveness));
// Build conservative main dex content after first round of tree shaking. This is used
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
index eb03451..c71d0d3 100644
--- a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -79,7 +79,7 @@
virtualFile.classes().size());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
- for (StartupClass<DexType> startupClass : startupOrderForWriting.getClasses()) {
+ for (StartupClass<DexType, DexMethod> startupClass : startupOrderForWriting.getClasses()) {
assert !startupClass.isSynthetic();
DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
if (definition != null) {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
index 3566ad1..7bbaba0 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.experimental.startup;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
@@ -22,7 +23,7 @@
}
@Override
- public Collection<StartupClass<DexType>> getClasses() {
+ public Collection<StartupClass<DexType, DexMethod>> getClasses() {
return Collections.emptyList();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
index 673e0de..4122aa0 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -25,16 +26,16 @@
public class NonEmptyStartupOrder extends StartupOrder {
- private final LinkedHashSet<StartupClass<DexType>> startupClasses;
+ private final LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses;
// Redundant sets to allow efficient querying without boxing.
private final Set<DexType> nonSyntheticStartupClasses = Sets.newIdentityHashSet();
private final Set<DexType> syntheticStartupClasses = Sets.newIdentityHashSet();
- NonEmptyStartupOrder(LinkedHashSet<StartupClass<DexType>> startupClasses) {
+ NonEmptyStartupOrder(LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses) {
assert !startupClasses.isEmpty();
this.startupClasses = startupClasses;
- for (StartupClass<DexType> startupClass : startupClasses) {
+ for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
if (startupClass.isSynthetic()) {
syntheticStartupClasses.add(startupClass.getReference());
} else {
@@ -66,7 +67,7 @@
}
@Override
- public Collection<StartupClass<DexType>> getClasses() {
+ public Collection<StartupClass<DexType, DexMethod>> getClasses() {
return startupClasses;
}
@@ -77,13 +78,13 @@
@Override
public StartupOrder rewrittenWithLens(GraphLens graphLens) {
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses =
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
new LinkedHashSet<>(startupClasses.size());
- for (StartupClass<DexType> startupClass : startupClasses) {
+ for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
rewrittenStartupClasses.add(
- StartupClass.<DexType>builder()
+ StartupClass.dexBuilder()
.setFlags(startupClass.getFlags())
- .setReference(graphLens.lookupType(startupClass.getReference()))
+ .setClassReference(graphLens.lookupType(startupClass.getReference()))
.build());
}
return createNonEmpty(rewrittenStartupClasses);
@@ -91,7 +92,7 @@
@Override
public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses =
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
new LinkedHashSet<>(startupClasses.size());
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
new IdentityHashMap<>();
@@ -105,7 +106,7 @@
}
}
}
- for (StartupClass<DexType> startupClass : startupClasses) {
+ for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
addStartupClass(
startupClass, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
}
@@ -114,8 +115,8 @@
}
private static void addStartupClass(
- StartupClass<DexType> startupClass,
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses,
+ StartupClass<DexType, DexMethod> startupClass,
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
AppView<?> appView) {
if (startupClass.isSynthetic()) {
@@ -131,14 +132,15 @@
}
private static boolean addClass(
- DexProgramClass clazz, LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses) {
+ DexProgramClass clazz,
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses) {
return rewrittenStartupClasses.add(
- StartupClass.<DexType>builder().setReference(clazz.getType()).build());
+ StartupClass.dexBuilder().setClassReference(clazz.getType()).build());
}
private static void addClassAndParentClasses(
DexType type,
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses,
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
AppView<?> appView) {
DexProgramClass definition = appView.app().programDefinitionFor(type);
if (definition != null) {
@@ -148,7 +150,7 @@
private static void addClassAndParentClasses(
DexProgramClass clazz,
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses,
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
AppView<?> appView) {
if (addClass(clazz, rewrittenStartupClasses)) {
addParentClasses(clazz, rewrittenStartupClasses, appView);
@@ -157,7 +159,7 @@
private static void addParentClasses(
DexProgramClass clazz,
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses,
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses,
AppView<?> appView) {
clazz.forEachImmediateSupertype(
supertype -> addClassAndParentClasses(supertype, rewrittenStartupClasses, appView));
@@ -165,12 +167,12 @@
@Override
public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
- LinkedHashSet<StartupClass<DexType>> rewrittenStartupClasses =
+ LinkedHashSet<StartupClass<DexType, DexMethod>> rewrittenStartupClasses =
new LinkedHashSet<>(startupClasses.size());
LazyBox<Set<DexType>> contextsOfLiveSynthetics =
new LazyBox<>(
() -> computeContextsOfLiveSynthetics(prunedItems.getPrunedApp(), syntheticItems));
- for (StartupClass<DexType> startupClass : startupClasses) {
+ for (StartupClass<DexType, DexMethod> startupClass : startupClasses) {
// Only prune non-synthetic classes, since the pruning of a class does not imply that all
// classes synthesized from it have been pruned.
if (startupClass.isSynthetic()) {
@@ -196,7 +198,8 @@
return contextsOfLiveSynthetics;
}
- private StartupOrder createNonEmpty(LinkedHashSet<StartupClass<DexType>> startupClasses) {
+ private StartupOrder createNonEmpty(
+ LinkedHashSet<StartupClass<DexType, DexMethod>> startupClasses) {
if (startupClasses.isEmpty()) {
assert false;
return empty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
index 9338d5f..0f76036 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
@@ -4,84 +4,47 @@
package com.android.tools.r8.experimental.startup;
-public class StartupClass<T> {
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
- private static final int FLAG_SYNTHETIC = 1;
+// TODO(b/238173796): When updating the compiler to have support for taking a list of startup
+// methods, this class may likely be removed along with the StartupItem class, so that only
+// StartupMethod remains.
+public class StartupClass<C, M> extends StartupItem<C, M, C> {
- private final int flags;
- private final T reference;
-
- public StartupClass(int flags, T reference) {
- this.flags = flags;
- this.reference = reference;
+ public StartupClass(int flags, C reference) {
+ super(flags, reference);
}
- public static <T> Builder<T> builder() {
+ public static <C, M> Builder<C, M> builder() {
return new Builder<>();
}
- public int getFlags() {
- return flags;
- }
-
- public T getReference() {
- return reference;
- }
-
- public boolean isSynthetic() {
- return (flags & FLAG_SYNTHETIC) != 0;
+ public static Builder<DexType, DexMethod> dexBuilder() {
+ return new Builder<>();
}
@Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- StartupClass<?> startupClass = (StartupClass<?>) obj;
- return flags == startupClass.flags && reference.equals(startupClass.reference);
+ public boolean isStartupClass() {
+ return true;
}
@Override
- public int hashCode() {
- assert flags <= 1;
- return (reference.hashCode() << 1) | flags;
+ public StartupClass<C, M> asStartupClass() {
+ return this;
}
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (isSynthetic()) {
- builder.append('S');
- }
- builder.append(reference);
- return builder.toString();
- }
+ public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
- public static class Builder<T> {
-
- private int flags;
- private T reference;
-
- public Builder<T> setFlags(int flags) {
- this.flags = flags;
- return this;
+ @Override
+ public Builder<C, M> setMethodReference(M reference) {
+ throw new Unreachable();
}
- public Builder<T> setReference(T reference) {
- this.reference = reference;
- return this;
- }
-
- public Builder<T> setSynthetic() {
- this.flags |= FLAG_SYNTHETIC;
- return this;
- }
-
- public StartupClass<T> build() {
- return new StartupClass<>(flags, reference);
+ @Override
+ public StartupClass<C, M> build() {
+ return new StartupClass<>(flags, classReference);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
index 9f16dce..9731423 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -6,9 +6,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
@@ -23,13 +21,10 @@
public class StartupConfiguration {
- private final List<StartupClass<DexType>> startupClasses;
- private final List<DexMethod> startupMethods;
+ private final List<StartupClass<DexType, DexMethod>> startupClasses;
- public StartupConfiguration(
- List<StartupClass<DexType>> startupClasses, List<DexMethod> startupMethods) {
+ public StartupConfiguration(List<StartupClass<DexType, DexMethod>> startupClasses) {
this.startupClasses = startupClasses;
- this.startupMethods = startupMethods;
}
public static Builder builder() {
@@ -79,120 +74,68 @@
public static StartupConfiguration createStartupConfigurationFromLines(
DexItemFactory dexItemFactory, Reporter reporter, List<String> startupDescriptors) {
- List<StartupClass<DexType>> startupClasses = new ArrayList<>();
- List<DexMethod> startupMethods = new ArrayList<>();
- for (String startupDescriptor : startupDescriptors) {
- if (startupDescriptor.isEmpty()) {
- continue;
- }
- StartupClass.Builder<DexType> startupClassBuilder = StartupClass.builder();
- startupDescriptor = parseSyntheticFlag(startupDescriptor, startupClassBuilder);
- int methodNameStartIndex = getMethodNameStartIndex(startupDescriptor);
- if (methodNameStartIndex >= 0) {
- DexMethod startupMethod =
- parseStartupMethodDescriptor(startupDescriptor, methodNameStartIndex, dexItemFactory);
- if (startupMethod != null) {
- startupClasses.add(
- startupClassBuilder.setReference(startupMethod.getHolderType()).build());
- startupMethods.add(startupMethod);
- } else {
- reporter.warning(
- new StringDiagnostic("Invalid descriptor for startup method: " + startupDescriptor));
- }
- } else {
- DexType startupClass = parseStartupClassDescriptor(startupDescriptor, dexItemFactory);
- if (startupClass != null) {
- startupClasses.add(startupClassBuilder.setReference(startupClass).build());
- } else {
- reporter.warning(
- new StringDiagnostic("Invalid descriptor for startup class: " + startupDescriptor));
- }
- }
- }
- return new StartupConfiguration(startupClasses, startupMethods);
- }
-
- public static String parseSyntheticFlag(
- String startupDescriptor, StartupClass.Builder<?> startupClassBuilder) {
- if (!startupDescriptor.isEmpty() && startupDescriptor.charAt(0) == 'S') {
- startupClassBuilder.setSynthetic();
- return startupDescriptor.substring(1);
- }
- return startupDescriptor;
- }
-
- private static int getMethodNameStartIndex(String startupDescriptor) {
- int arrowIndex = startupDescriptor.indexOf("->");
- return arrowIndex >= 0 ? arrowIndex + 2 : arrowIndex;
- }
-
- private static DexType parseStartupClassDescriptor(
- String startupClassDescriptor, DexItemFactory dexItemFactory) {
- if (DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
- return dexItemFactory.createType(startupClassDescriptor);
- } else {
- return null;
- }
- }
-
- private static DexMethod parseStartupMethodDescriptor(
- String startupMethodDescriptor, int methodNameStartIndex, DexItemFactory dexItemFactory) {
- String classDescriptor = startupMethodDescriptor.substring(0, methodNameStartIndex - 2);
- DexType classType = parseStartupClassDescriptor(classDescriptor, dexItemFactory);
- if (classType == null) {
- return null;
- }
-
- String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
- int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
- if (methodNameEndIndex <= 0) {
- return null;
- }
- String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
-
- String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
- DexProto proto = parseStartupMethodProto(protoDescriptor, dexItemFactory);
- return dexItemFactory.createMethod(classType, proto, methodName);
- }
-
- private static DexProto parseStartupMethodProto(
- String protoDescriptor, DexItemFactory dexItemFactory) {
- List<DexType> parameterTypes = new ArrayList<>();
- for (String parameterTypeDescriptor :
- DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
- parameterTypes.add(dexItemFactory.createType(parameterTypeDescriptor));
- }
- String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
- DexType returnType = dexItemFactory.createType(returnTypeDescriptor);
- return dexItemFactory.createProto(returnType, parameterTypes);
+ List<StartupClass<DexType, DexMethod>> startupClasses = new ArrayList<>();
+ StartupConfigurationParser.createDexParser(dexItemFactory)
+ .parseLines(
+ startupDescriptors,
+ startupClasses::add,
+ // TODO(b/238173796): Startup methods should be added as startup methods.
+ startupMethod ->
+ startupClasses.add(
+ StartupClass.dexBuilder()
+ .setClassReference(startupMethod.getReference().getHolderType())
+ .setFlags(startupMethod.getFlags())
+ .build()),
+ error ->
+ reporter.warning(
+ new StringDiagnostic(
+ "Invalid descriptor for startup class or method: " + error)));
+ return new StartupConfiguration(startupClasses);
}
public boolean hasStartupClasses() {
return !startupClasses.isEmpty();
}
- public List<StartupClass<DexType>> getStartupClasses() {
+ public List<StartupClass<DexType, DexMethod>> getStartupClasses() {
return startupClasses;
}
public static class Builder {
- private final ImmutableList.Builder<StartupClass<DexType>> startupClassesBuilder =
+ private final ImmutableList.Builder<StartupClass<DexType, DexMethod>> startupClassesBuilder =
ImmutableList.builder();
- private final ImmutableList.Builder<DexMethod> startupMethodsBuilder = ImmutableList.builder();
- public Builder addStartupClass(StartupClass<DexType> startupClass) {
+ public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
+ if (startupItem.isStartupClass()) {
+ return addStartupClass(startupItem.asStartupClass());
+ } else {
+ assert startupItem.isStartupMethod();
+ return addStartupMethod(startupItem.asStartupMethod());
+ }
+ }
+
+ public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
this.startupClassesBuilder.add(startupClass);
return this;
}
+ public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
+ // TODO(b/238173796): Startup methods should be added as startup methods.
+ return addStartupClass(
+ StartupClass.dexBuilder()
+ .setFlags(startupMethod.getFlags())
+ .setClassReference(startupMethod.getReference().getHolderType())
+ .build());
+ }
+
public Builder apply(Consumer<Builder> consumer) {
consumer.accept(this);
return this;
}
public StartupConfiguration build() {
- return new StartupConfiguration(startupClassesBuilder.build(), startupMethodsBuilder.build());
+ return new StartupConfiguration(startupClassesBuilder.build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
new file mode 100644
index 0000000..79ebc1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
@@ -0,0 +1,163 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class StartupConfigurationParser<C, M, T> {
+
+ interface MethodFactory<C, M, T> {
+
+ M createMethod(
+ C methodHolder, String methodName, List<T> methodParameterTypes, T methodReturnType);
+ }
+
+ private final Function<String, C> classFactory;
+ private final MethodFactory<C, M, T> methodFactory;
+ private final Function<String, T> typeFactory;
+
+ StartupConfigurationParser(
+ Function<String, C> classFactory,
+ MethodFactory<C, M, T> methodFactory,
+ Function<String, T> typeFactory) {
+ this.classFactory = classFactory;
+ this.methodFactory = methodFactory;
+ this.typeFactory = typeFactory;
+ }
+
+ public static StartupConfigurationParser<DexType, DexMethod, DexType> createDexParser(
+ DexItemFactory dexItemFactory) {
+ return new StartupConfigurationParser<>(
+ dexItemFactory::createType,
+ (methodHolder, methodName, methodParameters, methodReturnType) ->
+ dexItemFactory.createMethod(
+ methodHolder,
+ dexItemFactory.createProto(methodReturnType, methodParameters),
+ dexItemFactory.createString(methodName)),
+ dexItemFactory::createType);
+ }
+
+ public static StartupConfigurationParser<ClassReference, MethodReference, TypeReference>
+ createReferenceParser() {
+ return new StartupConfigurationParser<>(
+ Reference::classFromDescriptor, Reference::method, Reference::returnTypeFromDescriptor);
+ }
+
+ public void parseLines(
+ List<String> startupDescriptors,
+ Consumer<? super StartupClass<C, M>> startupClassConsumer,
+ Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
+ Consumer<String> parseErrorHandler) {
+ for (String startupDescriptor : startupDescriptors) {
+ if (!startupDescriptor.isEmpty()) {
+ parseLine(
+ startupDescriptor, startupClassConsumer, startupMethodConsumer, parseErrorHandler);
+ }
+ }
+ }
+
+ public void parseLine(
+ String startupDescriptor,
+ Consumer<? super StartupClass<C, M>> startupClassConsumer,
+ Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
+ Consumer<String> parseErrorHandler) {
+ StartupItem.Builder<C, M, ?> startupItemBuilder = StartupItem.builder();
+ startupDescriptor = parseSyntheticFlag(startupDescriptor, startupItemBuilder);
+ parseStartupClassOrMethod(
+ startupDescriptor,
+ startupItemBuilder,
+ startupClassConsumer,
+ startupMethodConsumer,
+ parseErrorHandler);
+ }
+
+ private static String parseSyntheticFlag(
+ String startupDescriptor, StartupItem.Builder<?, ?, ?> startupItemBuilder) {
+ if (!startupDescriptor.isEmpty() && startupDescriptor.charAt(0) == 'S') {
+ startupItemBuilder.setSynthetic();
+ return startupDescriptor.substring(1);
+ }
+ return startupDescriptor;
+ }
+
+ private void parseStartupClassOrMethod(
+ String startupDescriptor,
+ StartupItem.Builder<C, M, ?> startupItemBuilder,
+ Consumer<? super StartupClass<C, M>> startupClassConsumer,
+ Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
+ Consumer<String> parseErrorHandler) {
+ int arrowStartIndex = getArrowStartIndex(startupDescriptor);
+ if (arrowStartIndex >= 0) {
+ M startupMethod = parseStartupMethodDescriptor(startupDescriptor, arrowStartIndex);
+ if (startupMethod != null) {
+ startupMethodConsumer.accept(
+ startupItemBuilder.setMethodReference(startupMethod).buildStartupMethod());
+ } else {
+ parseErrorHandler.accept(startupDescriptor);
+ }
+ } else {
+ C startupClass = parseStartupClassDescriptor(startupDescriptor);
+ if (startupClass != null) {
+ startupClassConsumer.accept(
+ startupItemBuilder.setClassReference(startupClass).buildStartupClass());
+ } else {
+ parseErrorHandler.accept(startupDescriptor);
+ }
+ }
+ }
+
+ private static int getArrowStartIndex(String startupDescriptor) {
+ return startupDescriptor.indexOf("->");
+ }
+
+ private C parseStartupClassDescriptor(String startupClassDescriptor) {
+ if (DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
+ return classFactory.apply(startupClassDescriptor);
+ } else {
+ return null;
+ }
+ }
+
+ private M parseStartupMethodDescriptor(String startupMethodDescriptor, int arrowStartIndex) {
+ String classDescriptor = startupMethodDescriptor.substring(0, arrowStartIndex);
+ C methodHolder = parseStartupClassDescriptor(classDescriptor);
+ if (methodHolder == null) {
+ return null;
+ }
+
+ int methodNameStartIndex = arrowStartIndex + 2;
+ String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
+ int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
+ if (methodNameEndIndex <= 0) {
+ return null;
+ }
+ String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
+
+ String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
+ return parseStartupMethodProto(methodHolder, methodName, protoDescriptor);
+ }
+
+ private M parseStartupMethodProto(C methodHolder, String methodName, String protoDescriptor) {
+ List<T> parameterTypes = new ArrayList<>();
+ for (String parameterTypeDescriptor :
+ DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
+ parameterTypes.add(typeFactory.apply(parameterTypeDescriptor));
+ }
+ String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
+ T returnType = typeFactory.apply(returnTypeDescriptor);
+ return methodFactory.createMethod(methodHolder, methodName, parameterTypes, returnType);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
index aa97fa6..0678602 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
@@ -4,144 +4,160 @@
package com.android.tools.r8.experimental.startup;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
-import com.android.tools.r8.cf.code.CfConstString;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfReturnVoid;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfStaticFieldRead;
+import com.android.tools.r8.dex.code.DexInstruction;
+import com.android.tools.r8.dex.code.DexReturnVoid;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue.DexValueBoolean;
+import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
+import com.android.tools.r8.startup.generated.InstrumentationServerFactory;
+import com.android.tools.r8.startup.generated.InstrumentationServerImplFactory;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import org.objectweb.asm.Opcodes;
public class StartupInstrumentation {
- private final AppView<?> appView;
+ private final AppView<AppInfo> appView;
+ private final IRConverter converter;
private final DexItemFactory dexItemFactory;
private final StartupOptions options;
+ private final StartupReferences references;
- public StartupInstrumentation(AppView<?> appView) {
+ private StartupInstrumentation(AppView<AppInfo> appView) {
this.appView = appView;
+ this.converter = new IRConverter(appView, Timing.empty());
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options().getStartupOptions();
+ this.references = new StartupReferences(dexItemFactory);
}
- public void instrumentAllClasses(ExecutorService executorService) throws ExecutionException {
- instrumentClasses(appView.appInfo().classes(), executorService);
- }
-
- public boolean instrumentClasses(
- Collection<DexProgramClass> classes, ExecutorService executorService)
+ public static void run(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
- if (!appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
- return false;
+ if (appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+ StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
+ startupInstrumentation.instrumentAllClasses(executorService);
+ startupInstrumentation.injectStartupRuntimeLibrary(executorService);
}
- ThreadUtils.processItems(classes, this::internalInstrumentClass, executorService);
- return true;
}
- public void instrumentClass(DexProgramClass clazz) {
- if (!appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
- return;
+ private void instrumentAllClasses(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appView.appInfo().classes(), this::instrumentClass, executorService);
+ }
+
+ private void injectStartupRuntimeLibrary(ExecutorService executorService)
+ throws ExecutionException {
+ List<DexProgramClass> extraProgramClasses = createStartupRuntimeLibraryClasses();
+ converter.processClassesConcurrently(extraProgramClasses, executorService);
+
+ DexApplication newApplication =
+ appView.app().builder().addProgramClasses(extraProgramClasses).build();
+ appView.setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(newApplication),
+ appView.appInfo().getMainDexInfo()));
+ }
+
+ private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
+ DexProgramClass instrumentationServerImplClass =
+ InstrumentationServerImplFactory.createClass(dexItemFactory);
+ if (options.hasStartupInstrumentationTag()) {
+ instrumentationServerImplClass
+ .lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
+ .setStaticValue(DexValueBoolean.create(true));
+ instrumentationServerImplClass
+ .lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
+ .setStaticValue(
+ new DexValueString(
+ dexItemFactory.createString(options.getStartupInstrumentationTag())));
}
- internalInstrumentClass(clazz);
+
+ return ImmutableList.of(
+ InstrumentationServerFactory.createClass(dexItemFactory), instrumentationServerImplClass);
}
- private void internalInstrumentClass(DexProgramClass clazz) {
- ProgramMethod classInitializer = ensureClassInitializer(clazz);
- instrumentClassInitializer(classInitializer);
+ private void instrumentClass(DexProgramClass clazz) {
+ ensureClassInitializer(clazz);
+ clazz.forEachProgramMethod(this::instrumentMethod);
}
- private ProgramMethod ensureClassInitializer(DexProgramClass clazz) {
+ private void ensureClassInitializer(DexProgramClass clazz) {
if (!clazz.hasClassInitializer()) {
- int maxLocals = 0;
- int maxStack = 0;
ComputedApiLevel computedApiLevel =
appView.apiLevelCompute().computeInitialMinApiLevel(appView.options());
+ DexReturnVoid returnInstruction = new DexReturnVoid();
+ returnInstruction.setOffset(0);
clazz.addDirectMethod(
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.createForClassInitializer())
.setApiLevelForCode(computedApiLevel)
.setApiLevelForDefinition(computedApiLevel)
.setClassFileVersion(CfVersion.V1_6)
- .setCode(
- new CfCode(
- clazz.getType(), maxStack, maxLocals, ImmutableList.of(new CfReturnVoid())))
+ .setCode(new DexCode(0, 0, 0, new DexInstruction[] {returnInstruction}))
.setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
.build());
}
- return clazz.getProgramClassInitializer();
}
- private void instrumentClassInitializer(ProgramMethod classInitializer) {
- Code code = classInitializer.getDefinition().getCode();
- if (!code.isCfCode()) {
- // Should generally not happen.
- assert false;
- return;
- }
-
+ private void instrumentMethod(ProgramMethod method) {
+ DexMethod methodToInvoke;
+ DexMethod methodToPrint;
SyntheticItems syntheticItems = appView.getSyntheticItems();
- DexString message;
- if (syntheticItems.isSyntheticClass(classInitializer.getHolder())) {
+ if (syntheticItems.isSyntheticClass(method.getHolder())) {
Collection<DexType> synthesizingContexts =
- syntheticItems.getSynthesizingContextTypes(classInitializer.getHolderType());
+ syntheticItems.getSynthesizingContextTypes(method.getHolderType());
assert synthesizingContexts.size() == 1;
- message = synthesizingContexts.iterator().next().getDescriptor().prepend("S", dexItemFactory);
+ DexType synthesizingContext = synthesizingContexts.iterator().next();
+ methodToInvoke = references.addSyntheticMethod;
+ methodToPrint = method.getReference().withHolder(synthesizingContext, dexItemFactory);
} else {
- message = classInitializer.getHolderType().getDescriptor();
+ methodToInvoke = references.addNonSyntheticMethod;
+ methodToPrint = method.getReference();
}
- CfCode cfCode = code.asCfCode();
- List<CfInstruction> instructions;
- if (options.hasStartupInstrumentationTag()) {
- instructions = new ArrayList<>(4 + cfCode.getInstructions().size());
- instructions.add(
- new CfConstString(dexItemFactory.createString(options.getStartupInstrumentationTag())));
- instructions.add(new CfConstString(message));
- instructions.add(
- new CfInvoke(Opcodes.INVOKESTATIC, dexItemFactory.androidUtilLogMembers.i, false));
- instructions.add(new CfStackInstruction(Opcode.Pop));
- } else {
- instructions = new ArrayList<>(3 + cfCode.getInstructions().size());
- instructions.add(new CfStaticFieldRead(dexItemFactory.javaLangSystemMembers.out));
- instructions.add(new CfConstString(message));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKEVIRTUAL,
- dexItemFactory.javaIoPrintStreamMembers.printlnWithString,
- false));
- }
- instructions.addAll(cfCode.getInstructions());
- classInitializer.setCode(
- new CfCode(
- cfCode.getOriginalHolder(),
- Math.max(cfCode.getMaxStack(), 2),
- cfCode.getMaxLocals(),
- instructions,
- cfCode.getTryCatchRanges(),
- cfCode.getLocalVariables(),
- cfCode.getDiagnosticPosition(),
- cfCode.getMetadata()),
- appView);
+ IRCode code = method.buildIR(appView);
+ InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
+ instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
+
+ Value descriptorValue =
+ instructionIterator.insertConstStringInstruction(
+ appView, code, dexItemFactory.createString(methodToPrint.toSmaliString()));
+ instructionIterator.add(
+ InvokeStatic.builder()
+ .setMethod(methodToInvoke)
+ .setSingleArgument(descriptorValue)
+ .setPosition(Position.syntheticNone())
+ .build());
+ DexCode instrumentedCode =
+ new IRToDexFinalizer(appView, converter.deadCodeRemover)
+ .finalizeCode(code, BytecodeMetadataProvider.empty(), Timing.empty());
+ method.setCode(instrumentedCode, appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
new file mode 100644
index 0000000..c745c90
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
@@ -0,0 +1,144 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import java.util.function.Consumer;
+
+public abstract class StartupItem<C, M, R> {
+
+ private static final int FLAG_SYNTHETIC = 1;
+
+ protected final int flags;
+ protected final R reference;
+
+ public StartupItem(int flags, R reference) {
+ this.flags = flags;
+ this.reference = reference;
+ }
+
+ public boolean isStartupClass() {
+ return false;
+ }
+
+ public StartupClass<C, M> asStartupClass() {
+ return null;
+ }
+
+ public boolean isStartupMethod() {
+ return false;
+ }
+
+ public StartupMethod<C, M> asStartupMethod() {
+ return null;
+ }
+
+ public static <C, M> Builder<C, M, ?> builder() {
+ return new Builder<>();
+ }
+
+ public static Builder<DexType, DexMethod, ?> dexBuilder() {
+ return new Builder<>();
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ public R getReference() {
+ return reference;
+ }
+
+ public boolean isSynthetic() {
+ return (flags & FLAG_SYNTHETIC) != 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ StartupItem<?, ?, ?> startupItem = (StartupItem<?, ?, ?>) obj;
+ return flags == startupItem.flags && reference.equals(startupItem.reference);
+ }
+
+ @Override
+ public int hashCode() {
+ return (reference.hashCode() << 1) | flags;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (isSynthetic()) {
+ builder.append('S');
+ }
+ builder.append(reference);
+ return builder.toString();
+ }
+
+ public static class Builder<C, M, B extends Builder<C, M, B>> {
+
+ protected int flags;
+ protected C classReference;
+ protected M methodReference;
+
+ public B applyIf(boolean condition, Consumer<B> thenConsumer, Consumer<B> elseConsumer) {
+ if (condition) {
+ thenConsumer.accept(self());
+ } else {
+ elseConsumer.accept(self());
+ }
+ return self();
+ }
+
+ public B setFlags(int flags) {
+ this.flags = flags;
+ return self();
+ }
+
+ public B setClassReference(C reference) {
+ this.classReference = reference;
+ return self();
+ }
+
+ public B setMethodReference(M reference) {
+ this.methodReference = reference;
+ return self();
+ }
+
+ public B setSynthetic() {
+ this.flags |= FLAG_SYNTHETIC;
+ return self();
+ }
+
+ public StartupItem<C, M, ?> build() {
+ if (classReference != null) {
+ return buildStartupClass();
+ } else {
+ return buildStartupMethod();
+ }
+ }
+
+ public StartupClass<C, M> buildStartupClass() {
+ assert classReference != null;
+ return new StartupClass<>(flags, classReference);
+ }
+
+ public StartupMethod<C, M> buildStartupMethod() {
+ assert methodReference != null;
+ return new StartupMethod<>(flags, methodReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ public B self() {
+ return (B) this;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
new file mode 100644
index 0000000..3109611
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
@@ -0,0 +1,43 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+public class StartupMethod<C, M> extends StartupItem<C, M, M> {
+
+ public StartupMethod(int flags, M reference) {
+ super(flags, reference);
+ }
+
+ public static Builder<ClassReference, MethodReference> referenceBuilder() {
+ return new Builder<>();
+ }
+
+ @Override
+ public boolean isStartupMethod() {
+ return true;
+ }
+
+ @Override
+ public StartupMethod<C, M> asStartupMethod() {
+ return this;
+ }
+
+ public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
+
+ @Override
+ public Builder<C, M> setClassReference(C reference) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public StartupMethod<C, M> build() {
+ return new StartupMethod<>(flags, methodReference);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index 5d67d8c..1676991 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.experimental.startup;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
@@ -35,7 +36,7 @@
public abstract boolean contains(DexType type, SyntheticItems syntheticItems);
- public abstract Collection<StartupClass<DexType>> getClasses();
+ public abstract Collection<StartupClass<DexType, DexMethod>> getClasses();
public abstract boolean isEmpty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
new file mode 100644
index 0000000..0c2de77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
@@ -0,0 +1,31 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+
+public class StartupReferences {
+
+ final DexType instrumentationServerImplType;
+ final DexMethod addNonSyntheticMethod;
+ final DexMethod addSyntheticMethod;
+
+ StartupReferences(DexItemFactory dexItemFactory) {
+ instrumentationServerImplType =
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;");
+ addNonSyntheticMethod =
+ dexItemFactory.createMethod(
+ instrumentationServerImplType,
+ dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringType),
+ "addNonSyntheticMethod");
+ addSyntheticMethod =
+ dexItemFactory.createMethod(
+ instrumentationServerImplType,
+ dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringType),
+ "addSyntheticMethod");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 0a485e7..b8613b8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -78,6 +78,17 @@
.withItemArray(c -> c.instructions);
}
+ public DexCode(int registerSize, int insSize, int outsSize, DexInstruction[] instructions) {
+ this(
+ registerSize,
+ insSize,
+ outsSize,
+ instructions,
+ Try.EMPTY_ARRAY,
+ TryHandler.EMPTY_ARRAY,
+ null);
+ }
+
public DexCode(
int registerSize,
int insSize,
@@ -714,6 +725,8 @@
public static class Try extends DexItem implements StructuralItem<Try> {
+ public static final Try[] EMPTY_ARRAY = new Try[0];
+
public static final int NO_INDEX = -1;
public final int handlerOffset;
@@ -779,6 +792,8 @@
public static class TryHandler extends DexItem implements StructuralItem<TryHandler> {
+ public static final TryHandler[] EMPTY_ARRAY = new TryHandler[0];
+
public static final int NO_HANDLER = -1;
public final TypeAddrPair[] pairs;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 8fe1c7e..44506fd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2118,7 +2118,7 @@
}
public boolean isAppendCharSequenceMethod(DexMethod method) {
- return method == appendCharSequence;
+ return method == appendCharSequence || method == appendSubCharSequence;
}
public boolean isAppendObjectOrCharSequenceMethod(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index ef0e76e..0efe7d0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -407,6 +407,11 @@
.asArrayType();
}
+ public static ClassTypeElement stringClassType(AppView<?> appView) {
+ return fromDexType(appView.dexItemFactory().stringType, Nullability.maybeNull(), appView)
+ .asClassType();
+ }
+
public static ClassTypeElement classClassType(AppView<?> appView, Nullability nullability) {
return fromDexType(appView.dexItemFactory().classType, nullability, appView).asClassType();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 0cb7228..3dcfa35 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -125,6 +125,11 @@
return next();
}
+ default Instruction positionBeforeNextInstructionThatMatches(Predicate<Instruction> predicate) {
+ nextUntil(predicate);
+ return previous();
+ }
+
boolean replaceCurrentInstructionByNullCheckIfPossible(AppView<?> appView, ProgramMethod context);
default boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 36f717a..71d88d5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -158,8 +158,7 @@
// Finalize the desugaring of the processed classes. This may require processing (and
// reprocessing) of some methods.
List<ProgramMethod> needsProcessing =
- instructionDesugaringEventConsumer.finalizeDesugaring(
- appView, executorService, resultBuilder);
+ instructionDesugaringEventConsumer.finalizeDesugaring(appView, resultBuilder);
if (!needsProcessing.isEmpty()) {
// Create a new processor context to ensure unique method processing contexts.
methodProcessor.newWave();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 52dc8b3..33f3f28 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
@@ -104,6 +103,7 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -466,8 +466,6 @@
D8CfInstructionDesugaringEventConsumer desugaringEventConsumer,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
- new StartupInstrumentation(appView).instrumentClass(clazz);
-
// When converting all methods on a class always convert <clinit> first.
ProgramMethod classInitializer = clazz.getProgramClassInitializer();
@@ -931,6 +929,16 @@
}
}
+ public void processClassesConcurrently(
+ Collection<DexProgramClass> classes, ExecutorService executorService)
+ throws ExecutionException {
+ ProgramMethodSet wave = ProgramMethodSet.create();
+ for (DexProgramClass clazz : classes) {
+ clazz.forEachProgramMethod(wave::add);
+ }
+ processMethodsConcurrently(wave, executorService);
+ }
+
public void processMethodsConcurrently(ProgramMethodSet wave, ExecutorService executorService)
throws ExecutionException {
if (!wave.isEmpty()) {
@@ -1112,8 +1120,7 @@
codeRewriter.simplifyDebugLocals(code);
}
- if (appView.graphLens().hasCodeRewritings()) {
- assert lensCodeRewriter != null;
+ if (lensCodeRewriter != null) {
timing.begin("Lens rewrite");
lensCodeRewriter.rewrite(code, context, methodProcessor);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 15c214a..fcf37fb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClasspathClass;
@@ -41,8 +40,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -90,7 +87,6 @@
private final D8MethodProcessor methodProcessor;
- private final Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
private final Map<DexReference, InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges =
new LinkedHashMap<>();
private final List<LambdaClass> synthesizedLambdaClasses = new ArrayList<>();
@@ -100,18 +96,9 @@
this.methodProcessor = methodProcessor;
}
- private void acceptClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
-
- private void acceptMethod(ProgramMethod method) {
- acceptClass(method.getHolder());
- }
-
@Override
public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
// Intentionally empty. Methods are moved when processing the interface definition.
- acceptMethod(method);
}
@Override
@@ -126,25 +113,21 @@
@Override
public void acceptCollectionConversion(ProgramMethod arrayConversion) {
- acceptMethod(arrayConversion);
methodProcessor.scheduleMethodForProcessing(arrayConversion, this);
}
@Override
public void acceptCovariantRetargetMethod(ProgramMethod method) {
- acceptMethod(method);
methodProcessor.scheduleMethodForProcessing(method, this);
}
@Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
- acceptMethod(backportedMethod);
methodProcessor.scheduleMethodForProcessing(backportedMethod, this);
}
@Override
public void acceptRecordMethod(ProgramMethod method) {
- acceptMethod(method);
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -158,13 +141,11 @@
@Override
public void acceptRecordClass(DexProgramClass recordClass) {
- acceptClass(recordClass);
methodProcessor.scheduleDesugaredMethodsForProcessing(recordClass.programMethods());
}
@Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
- acceptClass(lambdaClass.getLambdaProgramClass());
synchronized (synthesizedLambdaClasses) {
synthesizedLambdaClasses.add(lambdaClass);
}
@@ -173,7 +154,6 @@
@Override
public void acceptConstantDynamicClass(
ConstantDynamicClass constantDynamicClass, ProgramMethod context) {
- acceptClass(constantDynamicClass.getConstantDynamicProgramClass());
synchronized (synthesizedConstantDynamicClasses) {
synthesizedConstantDynamicClasses.add(constantDynamicClass);
}
@@ -196,20 +176,17 @@
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
- acceptMethod(closeMethod);
methodProcessor.scheduleMethodForProcessing(closeMethod, this);
}
@Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
- acceptMethod(method);
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@Override
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
- acceptMethod(method);
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -225,31 +202,20 @@
@Override
public void acceptAPIConversion(ProgramMethod method) {
- acceptMethod(method);
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@Override
public void acceptCompanionClassClinit(ProgramMethod method) {
- acceptMethod(method);
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
public List<ProgramMethod> finalizeDesugaring(
- AppView<?> appView,
- ExecutorService executorService,
- ClassConverterResult.Builder classConverterResultBuilder)
- throws ExecutionException {
+ AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder) {
List<ProgramMethod> needsProcessing = new ArrayList<>();
finalizeInvokeSpecialDesugaring(appView, needsProcessing::add);
finalizeLambdaDesugaring(classConverterResultBuilder, needsProcessing::add);
finalizeConstantDynamicDesugaring(needsProcessing::add);
- if (new StartupInstrumentation(appView)
- .instrumentClasses(synthesizedClasses, executorService)) {
- for (DexProgramClass synthesizedClass : synthesizedClasses) {
- needsProcessing.add(synthesizedClass.getProgramClassInitializer());
- }
- }
return needsProcessing;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index d28ccaf..2dcf7a4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider.ArrayConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.WrapperConstructorCfCodeProvider;
@@ -46,6 +47,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
@@ -108,6 +110,35 @@
this.conversionCfProvider = new DesugaredLibraryConversionCfProvider(appView, this);
}
+ enum WrapperKind {
+ WRAPPER,
+ VIVIFIED_WRAPPER;
+
+ SyntheticKindSelector getKindSelector() {
+ if (this == WrapperKind.WRAPPER) {
+ return kinds1 -> kinds1.WRAPPER;
+ } else {
+ return kinds -> kinds.VIVIFIED_WRAPPER;
+ }
+ }
+
+ DexType getWrappingType(DexType type, DexType vivifiedType) {
+ if (this == WrapperKind.WRAPPER) {
+ return vivifiedType;
+ } else {
+ return type;
+ }
+ }
+
+ DexType getWrappedType(DexType type, DexType vivifiedType) {
+ if (this == WrapperKind.WRAPPER) {
+ return type;
+ } else {
+ return vivifiedType;
+ }
+ }
+ }
+
public DesugaredLibraryConversionCfProvider getConversionCfProvider() {
return conversionCfProvider;
}
@@ -363,14 +394,19 @@
}
assert context.isNotProgramClass();
Iterable<DexMethod> methods =
- appView.options().machineDesugaredLibrarySpecification.getWrappers().get(context.type);
+ appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getWrappers()
+ .get(context.type)
+ .getMethods();
assert methods != null;
ClasspathOrLibraryClass classpathOrLibraryContext = context.asClasspathOrLibraryClass();
DexType type = context.type;
DexType vivifiedType = vivifiedTypeFor(type);
DexClass wrapper =
ensureClasspathWrapper(
- kinds -> kinds.WRAPPER,
+ WrapperKind.WRAPPER,
vivifiedType,
type,
classpathOrLibraryContext,
@@ -379,7 +415,7 @@
conversionCfProvider::generateWrapperConversionWithoutCode);
DexClass vivifiedWrapper =
ensureClasspathWrapper(
- kinds -> kinds.VIVIFIED_WRAPPER,
+ WrapperKind.VIVIFIED_WRAPPER,
type,
vivifiedType,
classpathOrLibraryContext,
@@ -395,8 +431,8 @@
DexClass vivifiedWrapper;
DexClass wrapper;
assert appView.options().isDesugaredLibraryCompilation();
- wrapper = getExistingProgramWrapper(context, kinds -> kinds.WRAPPER);
- vivifiedWrapper = getExistingProgramWrapper(context, kinds -> kinds.VIVIFIED_WRAPPER);
+ wrapper = getExistingProgramWrapper(context, WrapperKind.WRAPPER);
+ vivifiedWrapper = getExistingProgramWrapper(context, WrapperKind.VIVIFIED_WRAPPER);
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
return new WrapperConversions(
@@ -404,9 +440,15 @@
getConversion(vivifiedWrapper, wrapperField.type, vivifiedWrapperField.type));
}
- private DexProgramClass getExistingProgramWrapper(
- DexClass context, SyntheticKindSelector kindSelector) {
- return appView.getSyntheticItems().getExistingFixedClass(kindSelector, context, appView);
+ private DexProgramClass getExistingProgramWrapper(DexClass context, WrapperKind kind) {
+ if (context.isEnum()) {
+ return appView
+ .getSyntheticItems()
+ .getExistingFixedClass(kinds -> kinds.ENUM_CONVERSION, context, appView);
+ }
+ return appView
+ .getSyntheticItems()
+ .getExistingFixedClass(kind.getKindSelector(), getWrapperContext(context, kind), appView);
}
private DexMethod getConversion(DexClass wrapper, DexType returnType, DexType argType) {
@@ -426,27 +468,32 @@
}
private DexProgramClass ensureProgramWrapper(
- SyntheticKindSelector kindSelector,
- DexType wrappingType,
- DexType wrappedType,
- DexProgramClass programContext,
+ DexType type,
+ DexProgramClass context,
+ WrapperKind kind,
DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
assert eventConsumer != null;
+ DexType vivifiedType = vivifiedTypeFor(type);
return appView
.getSyntheticItems()
.ensureFixedClass(
- kindSelector,
- programContext,
+ kind.getKindSelector(),
+ getWrapperContext(context, kind),
appView,
- builder -> buildWrapper(wrappingType, wrappedType, programContext, builder),
+ builder ->
+ buildWrapper(
+ kind.getWrappingType(type, vivifiedType),
+ kind.getWrappedType(type, vivifiedType),
+ context.isInterface(),
+ builder),
// The creation of virtual methods may require new wrappers, this needs to happen
// once the wrapper is created to avoid infinite recursion.
eventConsumer::acceptWrapperProgramClass);
}
private DexClasspathClass ensureClasspathWrapper(
- SyntheticKindSelector kindSelector,
+ WrapperKind kind,
DexType wrappingType,
DexType wrappedType,
ClasspathOrLibraryClass classpathOrLibraryContext,
@@ -457,13 +504,14 @@
return appView
.getSyntheticItems()
.ensureFixedClasspathClass(
- kindSelector,
- classpathOrLibraryContext,
+ kind.getKindSelector(),
+ getWrapperContext(classpathOrLibraryContext.asDexClass(), kind)
+ .asClasspathOrLibraryClass(),
appView,
builder -> {
DexEncodedField wrapperField =
buildWrapper(
- wrappingType, wrappedType, classpathOrLibraryContext.asDexClass(), builder);
+ wrappingType, wrappedType, classpathOrLibraryContext.isInterface(), builder);
builder.addMethod(
methodBuilder ->
buildConversionMethod(
@@ -474,21 +522,29 @@
eventConsumer::acceptWrapperClasspathClass);
}
- private void getExistingProgramConversionMethod(
- SyntheticKindSelector kindSelector,
+ private void synthesizeProgramConversionMethod(
+ WrapperKind kind,
DexProgramClass context,
+ List<DexType> subwrappers,
DexClass wrapper,
DexClass reverseWrapper) {
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField reverseWrapperField = getWrapperUniqueField(reverseWrapper);
+ List<DexMethod> subwrapperConvertList = new ArrayList<>();
+ for (DexType subwrapper : subwrappers) {
+ DexClass subwrapperClass = appView.definitionFor(subwrapper);
+ assert subwrapperClass != null;
+ DexProgramClass subwrapperWrapper = getExistingProgramWrapper(subwrapperClass, kind);
+ subwrapperConvertList.add(getConversion(subwrapperWrapper));
+ }
DexProto proto = factory.createProto(reverseWrapperField.type, wrapperField.type);
appView
.getSyntheticItems()
.ensureFixedClassMethod(
factory.convertMethodName,
proto,
- kindSelector,
- context,
+ kind.getKindSelector(),
+ getWrapperContext(context, kind).asProgramOrClasspathDefinition(),
appView,
ignored -> {},
methodBuilder ->
@@ -496,14 +552,27 @@
methodBuilder,
proto,
computeProgramConversionMethodCode(
- wrapperField, reverseWrapperField, context)));
+ wrapperField, reverseWrapperField, context, subwrapperConvertList)));
+ }
+
+ private DexMethod getConversion(DexProgramClass subwrapperWrapper) {
+ Iterator<DexEncodedMethod> iterator = subwrapperWrapper.directMethods().iterator();
+ DexEncodedMethod method;
+ do {
+ method = iterator.next();
+ } while (!method.isStatic());
+ assert method.getName() == factory.convertMethodName;
+ return method.getReference();
}
private CfCode computeProgramConversionMethodCode(
- DexField wrapperField, DexField reverseWrapperField, DexClass context) {
+ DexField wrapperField,
+ DexField reverseWrapperField,
+ DexClass context,
+ List<DexMethod> subwrapperConvertList) {
assert context.isProgramClass();
return new NullableConversionCfCodeProvider.WrapperConversionCfCodeProvider(
- appView, reverseWrapperField, wrapperField)
+ appView, reverseWrapperField, wrapperField, subwrapperConvertList)
.generateCfCode();
}
@@ -523,9 +592,8 @@
private DexEncodedField buildWrapper(
DexType wrappingType,
DexType wrappedType,
- DexClass clazz,
+ boolean isItf,
SyntheticClassBuilder<?, ?> builder) {
- boolean isItf = clazz.isInterface();
DexType superType = isItf ? factory.objectType : wrappingType;
List<DexType> interfaces =
isItf ? Collections.singletonList(wrappingType) : Collections.emptyList();
@@ -607,7 +675,8 @@
DexField field = wrappedValueField(holder, fieldType);
// Field is package private to be accessible from convert methods without a getter.
FieldAccessFlags fieldAccessFlags =
- FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
+ FieldAccessFlags.fromCfAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
return DexEncodedField.syntheticBuilder()
.setField(field)
.setAccessFlags(fieldAccessFlags)
@@ -635,7 +704,7 @@
librarySpecification
.getWrappers()
.forEach(
- (type, methods) -> {
+ (type, descriptor) -> {
DexClass validClassToWrap = getValidClassToWrap(type);
// In broken set-ups we can end up having a json files containing wrappers of non
// desugared classes. Such wrappers are not required since the class won't be
@@ -644,47 +713,65 @@
if (validClassToWrap.isEnum()) {
enumConverter.ensureProgramEnumConversionClass(validClassToWrap, eventConsumer);
} else {
- validClassesToWrap.put(validClassToWrap.asProgramClass(), methods);
- ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
+ validClassesToWrap.put(
+ validClassToWrap.asProgramClass(), descriptor.getMethods());
+ synthesizeProgramWrappersWithoutVirtualMethods(
+ validClassToWrap, descriptor.getSubwrappers(), eventConsumer);
}
}
});
validClassesToWrap.forEach(
(clazz, methods) ->
- ensureProgramWrappersVirtualMethods(clazz, methods, eventConsumer, processingContext));
+ synthesizeProgramWrappersVirtualMethods(
+ clazz, methods, eventConsumer, processingContext));
+ }
+
+ private DexClass getWrapperContext(DexClass context, WrapperKind kind) {
+ if (kind != WrapperKind.VIVIFIED_WRAPPER) {
+ return context;
+ }
+ WrapperDescriptor descriptor =
+ appView.options().machineDesugaredLibrarySpecification.getWrappers().get(context.type);
+ assert descriptor != null;
+ if (descriptor.hasNonPublicAccess()) {
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClassFromType(
+ kinds -> kinds.VIVIFIED,
+ vivifiedTypeFor(context.type),
+ appView,
+ ignored -> {},
+ ignored -> {});
+ }
+ return context;
}
// We generate first the two wrappers with the constructor method and the fields, then we
// the two conversion methods which requires the wrappers to know both fields.
- private void ensureProgramWrappersWithoutVirtualMethods(
- DexClass context, DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
+ private void synthesizeProgramWrappersWithoutVirtualMethods(
+ DexClass context,
+ List<DexType> subwrappers,
+ DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
assert eventConsumer != null;
assert context.isProgramClass();
DexType type = context.type;
- assert appView.options().isDesugaredLibraryCompilation();
DexProgramClass programContext = context.asProgramClass();
DexClass wrapper =
- ensureProgramWrapper(
- kinds -> kinds.WRAPPER, vivifiedTypeFor(type), type, programContext, eventConsumer);
+ ensureProgramWrapper(type, programContext, WrapperKind.WRAPPER, eventConsumer);
DexClass vivifiedWrapper =
- ensureProgramWrapper(
- kinds -> kinds.VIVIFIED_WRAPPER,
- type,
- vivifiedTypeFor(type),
- programContext,
- eventConsumer);
- getExistingProgramConversionMethod(
- kinds -> kinds.WRAPPER, programContext, wrapper, vivifiedWrapper);
- getExistingProgramConversionMethod(
- kinds -> kinds.VIVIFIED_WRAPPER, programContext, vivifiedWrapper, wrapper);
+ ensureProgramWrapper(type, programContext, WrapperKind.VIVIFIED_WRAPPER, eventConsumer);
+ synthesizeProgramConversionMethod(
+ WrapperKind.WRAPPER, programContext, subwrappers, wrapper, vivifiedWrapper);
+ synthesizeProgramConversionMethod(
+ WrapperKind.VIVIFIED_WRAPPER, programContext, subwrappers, vivifiedWrapper, wrapper);
}
- private void ensureProgramWrappersVirtualMethods(
+ private void synthesizeProgramWrappersVirtualMethods(
DexProgramClass context,
Iterable<DexMethod> methods,
CfClassSynthesizerDesugaringEventConsumer eventConsumer,
ClassSynthesisDesugaringContext processingContext) {
- DexProgramClass wrapper = getExistingProgramWrapper(context, kinds -> kinds.WRAPPER);
+ DexProgramClass wrapper = getExistingProgramWrapper(context, WrapperKind.WRAPPER);
DexEncodedField wrapperField = getWrapperUniqueEncodedField(wrapper);
wrapper.addVirtualMethods(
synthesizeVirtualMethodsForWrapper(
@@ -698,7 +785,7 @@
() -> processingContext.createUniqueContext(wrapper))));
wrapper.addVirtualMethods(synthesizeHashCodeAndEquals(wrapper, wrapperField));
DexProgramClass vivifiedWrapper =
- getExistingProgramWrapper(context, kinds -> kinds.VIVIFIED_WRAPPER);
+ getExistingProgramWrapper(context, WrapperKind.VIVIFIED_WRAPPER);
DexEncodedField vivifiedWrapperField = getWrapperUniqueEncodedField(vivifiedWrapper);
vivifiedWrapper.addVirtualMethods(
synthesizeVirtualMethodsForWrapper(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index f17235f..5c70c34 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -15,11 +15,13 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
public class HumanRewritingFlags {
@@ -443,6 +445,7 @@
}
public HumanRewritingFlags build() {
+ validate();
return new HumanRewritingFlags(
ImmutableMap.copyOf(rewritePrefix),
ImmutableSet.copyOf(dontRewritePrefix),
@@ -462,5 +465,19 @@
ImmutableMap.copyOf(amendLibraryMethod),
ImmutableMap.copyOf(amendLibraryField));
}
+
+ private void validate() {
+ SetView<DexType> dups =
+ Sets.intersection(customConversions.keySet(), wrapperConversions.keySet());
+ if (!dups.isEmpty()) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Invalid desugared library configuration. "
+ + "Duplicate types in custom conversions and wrapper conversions: "
+ + String.join(
+ ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())),
+ origin));
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 16b0b9f..4aa99b6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -145,7 +145,7 @@
return rewritingFlags.isEmulatedInterfaceRewrittenType(type);
}
- public Map<DexType, List<DexMethod>> getWrappers() {
+ public Map<DexType, WrapperDescriptor> getWrappers() {
return rewritingFlags.getWrappers();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index e125c53..5ad734b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -10,13 +10,12 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.IdentityHashMap;
-import java.util.List;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@@ -39,7 +38,7 @@
Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface,
Map<DexMethod, DexMethod[]> apiGenericTypesConversion,
Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces,
- Map<DexType, List<DexMethod>> wrappers,
+ LinkedHashMap<DexType, WrapperDescriptor> wrappers,
Map<DexType, DexType> legacyBackport,
Set<DexType> dontRetarget,
Map<DexType, CustomConversionDescriptor> customConversions,
@@ -101,8 +100,8 @@
// Emulated interface descriptors.
private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
- // Wrappers and the list of methods they implement.
- private final Map<DexType, List<DexMethod>> wrappers;
+ // Wrapper descriptors.
+ private final LinkedHashMap<DexType, WrapperDescriptor> wrappers;
private final Map<DexType, DexType> legacyBackport;
private final Set<DexType> dontRetarget;
@@ -160,7 +159,7 @@
return emulatedInterfaces;
}
- public Map<DexType, List<DexMethod>> getWrappers() {
+ public Map<DexType, WrapperDescriptor> getWrappers() {
return wrappers;
}
@@ -249,7 +248,7 @@
ImmutableMap.builder();
private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
ImmutableMap.builder();
- private final ImmutableMap.Builder<DexType, List<DexMethod>> wrappers = ImmutableMap.builder();
+ private final LinkedHashMap<DexType, WrapperDescriptor> wrappers = new LinkedHashMap<>();
private final ImmutableMap.Builder<DexType, DexType> legacyBackport = ImmutableMap.builder();
private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
@@ -308,8 +307,8 @@
apiGenericTypesConversion.put(method, conversions);
}
- public void addWrapper(DexType wrapperConversion, List<DexMethod> methods) {
- wrappers.put(wrapperConversion, ImmutableList.copyOf(methods));
+ public void addWrapper(DexType type, WrapperDescriptor descriptor) {
+ this.wrappers.put(type, descriptor);
}
public void putLegacyBackport(DexType src, DexType target) {
@@ -367,7 +366,7 @@
emulatedVirtualRetargetThroughEmulatedInterface.build(),
apiGenericTypesConversion.build(),
emulatedInterfaces.build(),
- wrappers.build(),
+ wrappers,
legacyBackport.build(),
dontRetarget.build(),
customConversions.build(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
new file mode 100644
index 0000000..2403b90
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
@@ -0,0 +1,34 @@
+// 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.ir.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import java.util.List;
+
+public class WrapperDescriptor {
+ private final List<DexMethod> methods;
+ private final List<DexType> subwrappers;
+ private final boolean nonPublicAccess;
+
+ public WrapperDescriptor(
+ List<DexMethod> methods, List<DexType> directSubtypes, boolean nonPublicAccess) {
+ this.methods = methods;
+ this.subwrappers = directSubtypes;
+ this.nonPublicAccess = nonPublicAccess;
+ }
+
+ public List<DexMethod> getMethods() {
+ return methods;
+ }
+
+ public List<DexType> getSubwrappers() {
+ return subwrappers;
+ }
+
+ public boolean hasNonPublicAccess() {
+ return nonPublicAccess;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
index c0d9157..e471367 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -12,14 +12,19 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -28,6 +33,7 @@
private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final AppInfoWithClassHierarchy appInfo;
private final Set<DexType> missingClasses = Sets.newIdentityHashSet();
+ private final Set<DexMethod> invalidMethods = Sets.newIdentityHashSet();
public HumanToMachineWrapperConverter(AppInfoWithClassHierarchy appInfo) {
this.appInfo = appInfo;
@@ -37,6 +43,84 @@
HumanRewritingFlags rewritingFlags,
MachineRewritingFlags.Builder builder,
BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
+ Map<DexType, WrapperDescriptorBuilder> descriptors = initializeDescriptors(rewritingFlags);
+ fillDescriptors(rewritingFlags, descriptors);
+ // The descriptors have to be ordered so that when processing a type, subtypes have been
+ // processed before.
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors =
+ orderDescriptors(descriptors);
+ clearIncompleteSubwrappers(orderedDescriptors, rewritingFlags.getWrapperConversions());
+ finalizeWrapperDescriptors(orderedDescriptors, builder);
+ warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
+ warnConsumer.accept(
+ "The following methods cannot be handled by the wrappers due to their flags: ",
+ invalidMethods);
+ }
+
+ private void clearIncompleteSubwrappers(
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors,
+ Map<DexType, Set<DexMethod>> wrapperConversions) {
+ // If the wrapper is incomplete, it may lead to runtime errors.
+ // We never try to specialize the wrapper to an incomplete wrapper for this reason.
+ for (WrapperDescriptorBuilder descriptor : orderedDescriptors.values()) {
+ List<DexType> toRemove = new ArrayList<>();
+ for (DexType subwrapper : descriptor.getSubwrappers()) {
+ if (!wrapperConversions.get(subwrapper).isEmpty()) {
+ toRemove.add(subwrapper);
+ }
+ }
+ descriptor.removeSubwrappers(toRemove);
+ }
+ }
+
+ private static class WrapperDescriptorBuilder {
+ private final List<DexMethod> methods = new ArrayList<>();
+ private final List<DexType> subwrappers = new ArrayList<>();
+ private boolean nonPublicAccess = false;
+
+ public WrapperDescriptorBuilder() {}
+
+ public List<DexMethod> getMethods() {
+ return methods;
+ }
+
+ public List<DexType> getSubwrappers() {
+ return subwrappers;
+ }
+
+ public void addSubwrapper(DexType type) {
+ subwrappers.add(type);
+ }
+
+ public void setNonPublicAccess() {
+ nonPublicAccess = true;
+ }
+
+ public WrapperDescriptor toWrapperDescriptor() {
+ methods.sort(DexMethod::compareTo);
+ subwrappers.sort(DexType::compareTo);
+ return new WrapperDescriptor(
+ ImmutableList.copyOf(methods), ImmutableList.copyOf(subwrappers), nonPublicAccess);
+ }
+
+ public void removeSubwrappers(List<DexType> toRemove) {
+ if (!toRemove.isEmpty()) {
+ subwrappers.removeAll(toRemove);
+ }
+ }
+ }
+
+ private Map<DexType, WrapperDescriptorBuilder> initializeDescriptors(
+ HumanRewritingFlags rewritingFlags) {
+ Map<DexType, WrapperDescriptorBuilder> descriptors = new IdentityHashMap<>();
+ for (DexType wrapperType : rewritingFlags.getWrapperConversions().keySet()) {
+ descriptors.put(wrapperType, new WrapperDescriptorBuilder());
+ }
+ return descriptors;
+ }
+
+ private void fillDescriptors(
+ HumanRewritingFlags rewritingFlags, Map<DexType, WrapperDescriptorBuilder> descriptors) {
rewritingFlags
.getWrapperConversions()
.forEach(
@@ -44,53 +128,96 @@
DexClass wrapperClass = appInfo.definitionFor(wrapperType);
if (wrapperClass == null) {
missingClasses.add(wrapperType);
+ descriptors.remove(wrapperType);
return;
}
- List<DexMethod> methods;
- if (wrapperClass.isEnum()) {
- methods = ImmutableList.of();
- } else {
- methods = allImplementedMethods(wrapperClass, excludedMethods);
- methods.sort(DexMethod::compareTo);
- }
- builder.addWrapper(wrapperType, methods);
+ WrapperDescriptorBuilder descriptor = descriptors.get(wrapperType);
+ fillDescriptors(wrapperClass, excludedMethods, descriptor, descriptors);
});
- warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
}
- private List<DexMethod> allImplementedMethods(
- DexClass wrapperClass, Set<DexMethod> excludedMethods) {
+ private LinkedHashMap<DexType, WrapperDescriptorBuilder> orderDescriptors(
+ Map<DexType, WrapperDescriptorBuilder> descriptors) {
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors = new LinkedHashMap<>();
+ List<DexType> preOrdered = new ArrayList<>(descriptors.keySet());
+ preOrdered.sort(DexType::compareTo);
+ LinkedList<DexType> workList = new LinkedList<>(preOrdered);
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ WrapperDescriptorBuilder descriptor = descriptors.get(dexType);
+ List<DexType> subwrappers = descriptor.getSubwrappers();
+ if (Iterables.all(subwrappers, orderedDescriptors::containsKey)) {
+ orderedDescriptors.put(dexType, descriptor);
+ } else {
+ workList.addLast(dexType);
+ }
+ }
+ return orderedDescriptors;
+ }
+
+ private void finalizeWrapperDescriptors(
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> descriptors,
+ MachineRewritingFlags.Builder builder) {
+ descriptors.forEach(
+ (type, descriptor) -> {
+ LinkedList<DexType> workList = new LinkedList<>(descriptor.getSubwrappers());
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ List<DexType> subwrappers = descriptors.get(dexType).getSubwrappers();
+ descriptor.getSubwrappers().removeAll(subwrappers);
+ workList.addAll(subwrappers);
+ }
+ builder.addWrapper(type, descriptor.toWrapperDescriptor());
+ });
+ }
+
+ private void fillDescriptors(
+ DexClass wrapperClass,
+ Set<DexMethod> excludedMethods,
+ WrapperDescriptorBuilder descriptor,
+ Map<DexType, WrapperDescriptorBuilder> descriptors) {
HashSet<Wrapper<DexMethod>> wrappers = new HashSet<>();
for (DexMethod excludedMethod : excludedMethods) {
wrappers.add(equivalence.wrap(excludedMethod));
}
LinkedList<DexClass> workList = new LinkedList<>();
- List<DexMethod> implementedMethods = new ArrayList<>();
+ List<DexMethod> implementedMethods = descriptor.getMethods();
workList.add(wrapperClass);
while (!workList.isEmpty()) {
DexClass dexClass = workList.removeFirst();
- for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
- if (!virtualMethod.isPrivateMethod()
- // Don't include hashCode and equals overrides, as hashCode and equals are added to
- // all wrappers regardless.
- && (!appInfo.dexItemFactory().objectMembers.hashCode.match(virtualMethod))
- && (!appInfo.dexItemFactory().objectMembers.equals.match(virtualMethod))) {
- assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
- boolean alreadyAdded = wrappers.contains(equivalence.wrap(virtualMethod.getReference()));
- // This looks quadratic but given the size of the collections met in practice for
- // desugared libraries (Max ~15) it does not matter.
- if (!alreadyAdded) {
- for (DexMethod alreadyImplementedMethod : implementedMethods) {
- if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
- alreadyAdded = true;
- break;
+ if (dexClass != wrapperClass && descriptors.containsKey(dexClass.type)) {
+ descriptors.get(dexClass.type).addSubwrapper(wrapperClass.type);
+ }
+ if (!wrapperClass.isEnum()) {
+ for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
+ if (!virtualMethod.isPrivateMethod()
+ // Don't include hashCode and equals overrides, as hashCode and equals are added to
+ // all wrappers regardless.
+ && (!appInfo.dexItemFactory().objectMembers.hashCode.match(virtualMethod))
+ && (!appInfo.dexItemFactory().objectMembers.equals.match(virtualMethod))) {
+ assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
+ boolean alreadyAdded =
+ wrappers.contains(equivalence.wrap(virtualMethod.getReference()));
+ // This looks quadratic but given the size of the collections met in practice for
+ // desugared libraries (Max ~15) it does not matter.
+ if (!alreadyAdded) {
+ for (DexMethod alreadyImplementedMethod : implementedMethods) {
+ if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
+ alreadyAdded = true;
+ break;
+ }
}
}
- }
- if (!alreadyAdded) {
- assert !virtualMethod.isFinal()
- : "Cannot wrap final method " + virtualMethod + " while wrapping " + wrapperClass;
- implementedMethods.add(virtualMethod.getReference());
+ if (!alreadyAdded) {
+ if (virtualMethod.isFinal() || virtualMethod.isPrivateMethod()) {
+ invalidMethods.add(virtualMethod.getReference());
+ } else {
+ if (!virtualMethod.isPublic()) {
+ descriptor.setNonPublicAccess();
+ }
+ implementedMethods.add(virtualMethod.getReference());
+ }
+ }
}
}
}
@@ -107,6 +234,5 @@
workList.add(superClass);
}
}
- return implementedMethods;
}
}
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 d5168da..92467a3 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
@@ -723,6 +723,30 @@
assert resolutionResult.isSuccessfulMemberResolutionResult();
LookupMethodTarget lookupMethodTarget =
resolutionResult.lookupVirtualDispatchTarget(clazz, appInfo);
+ if (lookupMethodTarget == null) {
+ // This should not happen, but is does in b/237507594.
+ // We have no reproduction for the issue.
+ // We try to raise a warning here to help investigating:
+ // It could be an issue related to array#clone, or this can be due to a missing/invalid class.
+ assert false;
+ appView
+ .options()
+ .reporter
+ .warning(
+ "The class processor was not able to look-up the default method "
+ + method
+ + " in the class "
+ + clazz
+ + " (Single resolution: "
+ + resolutionResult.isSingleResolution()
+ + "; resolution pair: "
+ + resolutionResult.getResolutionPair()
+ + "). Please report this issue in the D8/R8 bug tracker at"
+ + " https://issuetracker.google.com/issues/237507594.");
+ // To be able to resume compilation we add a NoSuchMethodErrorThrowingMethod.
+ addNoSuchMethodErrorThrowingMethod(method, clazz);
+ return;
+ }
DexClassAndMethod virtualDispatchTarget = lookupMethodTarget.getTarget();
assert virtualDispatchTarget != null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
index 05a91e5..fd6ae9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
@@ -7,6 +7,7 @@
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.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -26,6 +27,18 @@
Instruction instruction,
StringBuilderOracle oracle);
+ default boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
+ return false;
+ }
+
+ default boolean isReplaceArgumentByStringConcat() {
+ return false;
+ }
+
+ default ReplaceArgumentByStringConcat asReplaceArgumentByStringConcat() {
+ return null;
+ }
+
/** The RemoveStringBuilderAction will simply remove the instruction completely. */
class RemoveStringBuilderAction implements StringBuilderAction {
@@ -38,22 +51,17 @@
InstructionListIterator iterator,
Instruction instruction,
StringBuilderOracle oracle) {
- assert oracle.isModeledStringBuilderInstruction(
- instruction,
- value ->
- value.getType().isClassType()
- && oracle.isStringBuilderType(value.getType().asClassType().getClassType()));
- if (oracle.isAppend(instruction) && instruction.outValue() != null) {
- // Append will return the string builder instance. Before removing, ensure that
- // all users of the output values uses the receiver.
- instruction.outValue().replaceUsers(instruction.getFirstOperand());
- }
- iterator.removeOrReplaceByDebugLocalRead();
+ removeStringBuilderInstruction(iterator, instruction, oracle);
}
static RemoveStringBuilderAction getInstance() {
return INSTANCE;
}
+
+ @Override
+ public boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
+ return true;
+ }
}
/**
@@ -102,28 +110,7 @@
Instruction previous = iterator.previous();
InvokeMethodWithReceiver invoke = previous.asInvokeMethodWithReceiver();
assert invoke != null;
- // If the block has catch handlers, inserting a constant string in the same block as the
- // append violates our block representation in DEX since constant string is throwing. If the
- // append is in a block with catch handlers, we simply insert a new constant string in the
- // entry block after all arguments.
- Value value;
- if (!invoke.getBlock().hasCatchHandlers()) {
- value =
- iterator.insertConstStringInstruction(
- appView, code, appView.dexItemFactory().createString(replacement));
- } else {
- InstructionListIterator stringInsertIterator = code.entryBlock().listIterator(code);
- while (stringInsertIterator.hasNext()) {
- Instruction next = stringInsertIterator.next();
- if (!next.isArgument()) {
- stringInsertIterator.previous();
- break;
- }
- }
- value =
- stringInsertIterator.insertConstStringInstruction(
- appView, code, appView.dexItemFactory().createString(replacement));
- }
+ Value value = insertStringConstantInstruction(appView, code, iterator, invoke, replacement);
iterator.next();
DexMethod invokedMethod = invoke.getInvokedMethod();
if (invoke.isInvokeConstructor(appView.dexItemFactory())) {
@@ -146,21 +133,16 @@
}
}
+ @Override
+ public boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
+ return true;
+ }
+
private boolean isAppendWithString(DexMethod method, DexItemFactory factory) {
return factory.stringBufferMethods.isAppendStringMethod(method)
|| factory.stringBuilderMethods.isAppendStringMethod(method);
}
- private DexMethod getConstructorWithStringParameter(
- DexMethod invokedMethod, DexItemFactory factory) {
- if (invokedMethod.getHolderType() == factory.stringBufferType) {
- return factory.stringBufferMethods.stringConstructor;
- } else {
- assert invokedMethod.getHolderType() == factory.stringBuilderType;
- return factory.stringBuilderMethods.stringConstructor;
- }
- }
-
private DexMethod getAppendWithStringParameter(
DexMethod invokedMethod, DexItemFactory factory) {
if (invokedMethod.getHolderType() == factory.stringBufferType) {
@@ -171,4 +153,253 @@
}
}
}
+
+ class ReplaceByExistingString implements StringBuilderAction {
+
+ private final Value existingString;
+
+ public ReplaceByExistingString(Value existingString) {
+ this.existingString = existingString;
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ instruction.outValue().replaceUsers(existingString);
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
+
+ @Override
+ public boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
+ return true;
+ }
+ }
+
+ class ReplaceByStringConcat implements StringBuilderAction {
+
+ private final Value first;
+ private final Value second;
+
+ private final String newConstant;
+
+ private ReplaceByStringConcat(Value first, Value second, String newConstant) {
+ assert first != null || newConstant != null;
+ assert second != null || newConstant != null;
+ this.first = first;
+ this.second = second;
+ this.newConstant = newConstant;
+ }
+
+ public static ReplaceByStringConcat replaceByValues(Value first, Value second) {
+ return new ReplaceByStringConcat(first, second, null);
+ }
+
+ public static ReplaceByStringConcat replaceByNewConstantConcatValue(
+ String newConstant, Value second) {
+ return new ReplaceByStringConcat(null, second, newConstant);
+ }
+
+ public static ReplaceByStringConcat replaceByValueConcatNewConstant(
+ Value first, String newConstant) {
+ return new ReplaceByStringConcat(first, null, newConstant);
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ Value constString = null;
+ if (newConstant != null) {
+ Instruction previous = iterator.previous();
+ constString =
+ insertStringConstantInstruction(appView, code, iterator, previous, newConstant);
+ iterator.next();
+ }
+ assert first != null || constString != null;
+ assert second != null || constString != null;
+ // To ensure that we do not fail narrowing when evaluating String.concat, we mark the type
+ // as maybe null.
+ iterator.replaceCurrentInstruction(
+ InvokeVirtual.builder()
+ .setFreshOutValue(
+ code, TypeElement.stringClassType(appView), instruction.getLocalInfo())
+ .setMethod(appView.dexItemFactory().stringMembers.concat)
+ .setArguments(
+ ImmutableList.of(
+ first != null ? first : constString, second != null ? second : constString))
+ .build());
+ }
+ }
+
+ class ReplaceArgumentByExistingString implements StringBuilderAction {
+
+ private final Value string;
+
+ public ReplaceArgumentByExistingString(Value string) {
+ this.string = string;
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ instruction.replaceValue(1, string);
+ }
+
+ @Override
+ public boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
+ return true;
+ }
+ }
+
+ class ReplaceArgumentByStringConcat implements StringBuilderAction {
+
+ private final Value first;
+ private final Value second;
+ private final String newConstant;
+ private final Value outValue;
+ private boolean removeInstruction;
+
+ private ReplaceArgumentByStringConcat(
+ Value first, Value second, String newConstant, Value outValue) {
+ assert first != null || newConstant != null;
+ assert second != null || newConstant != null;
+ this.first = first;
+ this.second = second;
+ this.newConstant = newConstant;
+ this.outValue = outValue;
+ }
+
+ public static ReplaceArgumentByStringConcat replaceByValues(
+ Value first, Value second, Value outValue) {
+ return new ReplaceArgumentByStringConcat(first, second, null, outValue);
+ }
+
+ public static ReplaceArgumentByStringConcat replaceByNewConstantConcatValue(
+ String newConstant, Value second, Value outValue) {
+ return new ReplaceArgumentByStringConcat(null, second, newConstant, outValue);
+ }
+
+ public static ReplaceArgumentByStringConcat replaceByValueConcatNewConstant(
+ Value first, String newConstant, Value outValue) {
+ return new ReplaceArgumentByStringConcat(first, null, newConstant, outValue);
+ }
+
+ public void setRemoveInstruction() {
+ removeInstruction = true;
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ assert instruction.isInvokeMethod();
+ assert instruction.inValues().size() == 2;
+ Instruction previous = iterator.previous();
+ assert previous == instruction;
+ Value constString = null;
+ if (newConstant != null) {
+ constString =
+ insertStringConstantInstruction(appView, code, iterator, previous, newConstant);
+ }
+ assert first != null || constString != null;
+ assert second != null || constString != null;
+ InvokeVirtual stringConcat =
+ InvokeVirtual.builder()
+ .setMethod(appView.dexItemFactory().stringMembers.concat)
+ .setOutValue(outValue)
+ .setArguments(
+ ImmutableList.of(
+ first != null ? first : constString, second != null ? second : constString))
+ .setPosition(instruction.getPosition())
+ .build();
+ iterator.add(stringConcat);
+ Instruction next = iterator.next();
+ assert next == instruction;
+ if (removeInstruction) {
+ removeStringBuilderInstruction(iterator, instruction, oracle);
+ } else {
+ instruction.replaceValue(1, outValue);
+ }
+ }
+
+ @Override
+ public boolean isReplaceArgumentByStringConcat() {
+ return true;
+ }
+
+ @Override
+ public ReplaceArgumentByStringConcat asReplaceArgumentByStringConcat() {
+ return this;
+ }
+ }
+
+ static DexMethod getConstructorWithStringParameter(
+ DexMethod invokedMethod, DexItemFactory factory) {
+ if (invokedMethod.getHolderType() == factory.stringBufferType) {
+ return factory.stringBufferMethods.stringConstructor;
+ } else {
+ assert invokedMethod.getHolderType() == factory.stringBuilderType;
+ return factory.stringBuilderMethods.stringConstructor;
+ }
+ }
+
+ static Value insertStringConstantInstruction(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ String newString) {
+ // If the block has catch handlers, inserting a constant string in the same block as the
+ // append violates our block representation in DEX since constant string is throwing. If the
+ // append is in a block with catch handlers, we simply insert a new constant string in the
+ // entry block after all arguments.
+ Value value;
+ if (!instruction.getBlock().hasCatchHandlers()) {
+ value =
+ iterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(newString));
+ } else {
+ InstructionListIterator stringInsertIterator = code.entryBlock().listIterator(code);
+ while (stringInsertIterator.hasNext()) {
+ Instruction next = stringInsertIterator.next();
+ if (!next.isArgument()) {
+ stringInsertIterator.previous();
+ break;
+ }
+ }
+ value =
+ stringInsertIterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(newString));
+ }
+ return value;
+ }
+
+ static void removeStringBuilderInstruction(
+ InstructionListIterator iterator, Instruction instruction, StringBuilderOracle oracle) {
+ assert oracle.isModeledStringBuilderInstruction(
+ instruction,
+ value ->
+ value.getType().isClassType()
+ && oracle.isStringBuilderType(value.getType().asClassType().getClassType()));
+ if (oracle.isAppend(instruction) && instruction.outValue() != null) {
+ // Append will return the string builder instance. Before removing, ensure that
+ // all users of the output values uses the receiver.
+ instruction.outValue().replaceUsers(instruction.getFirstOperand());
+ }
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index ee85c13..407717c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisOptions;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraproceduralDataflowAnalysis;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -33,7 +34,7 @@
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.EscapeNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
-import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppendNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.LoopNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.NewInstanceNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.SplitReferenceNode;
@@ -107,6 +108,7 @@
stringBuilderAction.perform(appView, code, it, instruction, oracle);
}
}
+ code.removeAllDeadAndTrivialPhis();
}
private static class StringBuilderGraphState {
@@ -273,7 +275,13 @@
Value receiver = invoke.getReceiver();
if (oracle.isInit(instruction)) {
InitNode initNode = createInitNode(instruction.asInvokeDirect());
- initNode.setConstantArgument(oracle.getConstantArgument(instruction));
+ String constantArgument = oracle.getConstantArgument(instruction);
+ initNode.setConstantArgument(constantArgument);
+ if (constantArgument == null
+ && oracle.isStringConstructor(instruction)
+ && invoke.getFirstNonReceiverArgument().isNeverNull()) {
+ initNode.setNonConstantArgument(invoke.getFirstNonReceiverArgument());
+ }
if (invoke.arguments().size() == 2) {
Value arg = invoke.getOperand(1);
if (oracle.hasStringBuilderType(arg)) {
@@ -288,9 +296,15 @@
escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
} else if (oracle.isAppend(instruction)) {
AppendNode appendNode = createAppendNode(instruction.asInvokeVirtual());
- appendNode.setConstantArgument(oracle.getConstantArgument(instruction));
- Value arg = invoke.getFirstNonReceiverArgument().getAliasedValue();
- if (oracle.hasStringBuilderType(arg)) {
+ String constantArgument = oracle.getConstantArgument(instruction);
+ appendNode.setConstantArgument(constantArgument);
+ Value arg = invoke.getFirstNonReceiverArgument();
+ if (constantArgument == null
+ && oracle.isAppendString(instruction)
+ && arg.isNeverNull()) {
+ appendNode.setNonConstantArgument(arg);
+ }
+ if (oracle.hasStringBuilderType(arg.getAliasedValue())) {
insertImplicitToStringNode(
arg, instruction, appendNode, escapeState, nodeConsumer);
}
@@ -341,7 +355,7 @@
private void insertImplicitToStringNode(
Value value,
Instruction instruction,
- InitOrAppend node,
+ InitOrAppendNode node,
StringBuilderEscapeState escapeState,
BiConsumer<Value, StringBuilderNode> nodeConsumer) {
assert escapeState.isLiveStringBuilder(value);
@@ -544,7 +558,14 @@
MunchingState munchingState =
new MunchingState(
- actions, escaping, inspectingCapacity, looping, materializing, newInstances, oracle);
+ actions,
+ escaping,
+ inspectingCapacity,
+ looping,
+ materializing,
+ newInstances,
+ oracle,
+ () -> code.createValue(TypeElement.stringClassType(appView)));
boolean keepMunching = true;
for (int i = 0; i < NUMBER_OF_MUNCHING_PASSES && keepMunching; i++) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
index c189299..033a17c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -27,7 +28,15 @@
StringBuilderInstruction asStringBuilderInstructionNode();
}
- interface InitOrAppend extends StringBuilderInstruction {
+ interface InitOrAppendNode extends StringBuilderInstruction {
+
+ boolean isInitNode();
+
+ boolean isAppendNode();
+
+ InitNode asInitNode();
+
+ AppendNode asAppendNode();
boolean hasConstantArgument();
@@ -38,6 +47,24 @@
void setImplicitToStringNode(ImplicitToStringNode node);
ImplicitToStringNode getImplicitToStringNode();
+
+ boolean hasNonConstantArgument();
+
+ void setNonConstantArgument(Value value);
+
+ Value getNonConstantArgument();
+
+ default boolean hasConstantOrNonConstantArgument() {
+ return hasConstantArgument() || hasNonConstantArgument();
+ }
+
+ boolean hasSingleSuccessor();
+
+ boolean hasSinglePredecessor();
+
+ StringBuilderNode getSingleSuccessor();
+
+ StringBuilderNode getSinglePredecessor();
}
private final Set<StringBuilderNode> successors = Sets.newLinkedHashSet();
@@ -69,11 +96,11 @@
return false;
}
- boolean isInitNode() {
+ public boolean isInitNode() {
return false;
}
- boolean isAppendNode() {
+ public boolean isAppendNode() {
return false;
}
@@ -105,15 +132,15 @@
return null;
}
- InitNode asInitNode() {
+ public InitNode asInitNode() {
return null;
}
- AppendNode asAppendNode() {
+ public AppendNode asAppendNode() {
return null;
}
- InitOrAppend asInitOrAppend() {
+ InitOrAppendNode asInitOrAppend() {
return null;
}
@@ -141,11 +168,11 @@
return isDead;
}
- boolean hasSingleSuccessor() {
+ public boolean hasSingleSuccessor() {
return successors.size() == 1;
}
- StringBuilderNode getSingleSuccessor() {
+ public StringBuilderNode getSingleSuccessor() {
assert hasSingleSuccessor();
return successors.iterator().next();
}
@@ -159,11 +186,11 @@
return successors;
}
- boolean hasSinglePredecessor() {
+ public boolean hasSinglePredecessor() {
return predecessors.size() == 1;
}
- StringBuilderNode getSinglePredecessor() {
+ public StringBuilderNode getSinglePredecessor() {
assert hasSinglePredecessor();
return predecessors.iterator().next();
}
@@ -278,18 +305,19 @@
/** An initNode is where a StringBuilder/StringBuffer is initialized. */
static class InitNode extends StringBuilderNode
- implements InitOrAppend, StringBuilderInstruction {
+ implements InitOrAppendNode, StringBuilderInstruction {
private final InvokeDirect instruction;
private ImplicitToStringNode implicitToStringNode;
private String constantArgument;
+ private Value nonConstantArgument;
private InitNode(InvokeDirect instruction) {
this.instruction = instruction;
}
@Override
- boolean isInitNode() {
+ public boolean isInitNode() {
return true;
}
@@ -299,12 +327,12 @@
}
@Override
- InitNode asInitNode() {
+ public InitNode asInitNode() {
return this;
}
@Override
- InitOrAppend asInitOrAppend() {
+ InitOrAppendNode asInitOrAppend() {
return this;
}
@@ -329,16 +357,6 @@
}
@Override
- public void setImplicitToStringNode(ImplicitToStringNode node) {
- implicitToStringNode = node;
- }
-
- @Override
- public ImplicitToStringNode getImplicitToStringNode() {
- return implicitToStringNode;
- }
-
- @Override
public String getConstantArgument() {
return constantArgument;
}
@@ -347,22 +365,53 @@
public boolean hasConstantArgument() {
return constantArgument != null;
}
+
+ @Override
+ public void setNonConstantArgument(Value value) {
+ this.nonConstantArgument = value;
+ }
+
+ @Override
+ public void setImplicitToStringNode(ImplicitToStringNode node) {
+ implicitToStringNode = node;
+ }
+
+ @Override
+ public ImplicitToStringNode getImplicitToStringNode() {
+ return implicitToStringNode;
+ }
+
+ @Override
+ public boolean hasNonConstantArgument() {
+ return nonConstantArgument != null;
+ }
+
+ @Override
+ public Value getNonConstantArgument() {
+ assert nonConstantArgument != null;
+ return nonConstantArgument;
+ }
+
+ boolean isConstructorInvokeSideEffectFree(StringBuilderOracle oracle) {
+ return oracle.isConstructorInvokeSideEffectFree(instruction);
+ }
}
/** AppendNodes are StringBuilder.append or StringBuffer.append. */
static class AppendNode extends StringBuilderNode
- implements InitOrAppend, StringBuilderInstruction {
+ implements InitOrAppendNode, StringBuilderInstruction {
private final InvokeVirtual instruction;
private ImplicitToStringNode implicitToStringNode;
private String constantArgument;
+ private Value nonConstantArgument;
private AppendNode(InvokeVirtual instruction) {
this.instruction = instruction;
}
@Override
- boolean isAppendNode() {
+ public boolean isAppendNode() {
return true;
}
@@ -372,12 +421,12 @@
}
@Override
- AppendNode asAppendNode() {
+ public AppendNode asAppendNode() {
return this;
}
@Override
- InitOrAppend asInitOrAppend() {
+ InitOrAppendNode asInitOrAppend() {
return this;
}
@@ -412,6 +461,21 @@
}
@Override
+ public boolean hasNonConstantArgument() {
+ return nonConstantArgument != null;
+ }
+
+ @Override
+ public void setNonConstantArgument(Value value) {
+ this.nonConstantArgument = value;
+ }
+
+ @Override
+ public Value getNonConstantArgument() {
+ return nonConstantArgument;
+ }
+
+ @Override
public String getConstantArgument() {
return constantArgument;
}
@@ -540,13 +604,13 @@
*/
static class ImplicitToStringNode extends StringBuilderNode {
- private final InitOrAppend initOrAppend;
+ private final InitOrAppendNode initOrAppend;
- ImplicitToStringNode(InitOrAppend initOrAppend) {
+ ImplicitToStringNode(InitOrAppendNode initOrAppend) {
this.initOrAppend = initOrAppend;
}
- public InitOrAppend getInitOrAppend() {
+ public InitOrAppendNode getInitOrAppend() {
return initOrAppend;
}
@@ -601,7 +665,7 @@
return new OtherStringBuilderNode(instruction);
}
- static ImplicitToStringNode createImplicitToStringNode(InitOrAppend otherNode) {
+ static ImplicitToStringNode createImplicitToStringNode(InitOrAppendNode otherNode) {
return new ImplicitToStringNode(otherNode);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
index 0659a8a..5cdcc6b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
@@ -8,19 +8,26 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.AppendWithNewConstantString;
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.RemoveStringBuilderAction;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceArgumentByExistingString;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceArgumentByStringConcat;
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByConstantString;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByExistingString;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByStringConcat;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
-import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppendNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.NewInstanceNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.StringBuilderInstruction;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ToStringNode;
import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Supplier;
/**
* StringBuilderNodeMuncher is a classic munching algorithm that will try to remove nodes on string
@@ -42,6 +49,7 @@
private final Map<StringBuilderNode, Set<StringBuilderNode>> materializingInstructions;
private final Map<StringBuilderNode, NewInstanceNode> newInstances;
private final Map<Value, String> optimizedStrings = new IdentityHashMap<>();
+ private final Supplier<Value> newValueSupplier;
MunchingState(
Map<Instruction, StringBuilderAction> actions,
@@ -50,7 +58,8 @@
Set<StringBuilderNode> looping,
Map<StringBuilderNode, Set<StringBuilderNode>> materializingInstructions,
Map<StringBuilderNode, NewInstanceNode> newInstances,
- StringBuilderOracle oracle) {
+ StringBuilderOracle oracle,
+ Supplier<Value> newValueSupplier) {
this.actions = actions;
this.escaping = escaping;
this.inspectingCapacity = inspectingCapacity;
@@ -58,6 +67,7 @@
this.materializingInstructions = materializingInstructions;
this.newInstances = newInstances;
this.oracle = oracle;
+ this.newValueSupplier = newValueSupplier;
}
public NewInstanceNode getNewInstanceNode(StringBuilderNode root) {
@@ -67,6 +77,18 @@
public boolean isLooping(StringBuilderNode root) {
return looping.contains(root);
}
+
+ public boolean isEscaping(StringBuilderNode root) {
+ return escaping.contains(root);
+ }
+
+ public boolean isInspecting(StringBuilderNode root) {
+ return inspectingCapacity.contains(root);
+ }
+
+ public Value getNewOutValue() {
+ return newValueSupplier.get();
+ }
}
private interface PeepholePattern {
@@ -93,30 +115,32 @@
@Override
public boolean optimize(
StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
- if (!currentNode.isAppendNode()) {
+ AppendNode appendNode = currentNode.asAppendNode();
+ if (appendNode == null || !appendNode.hasSinglePredecessor()) {
return false;
}
- String currentConstantArgument = getConstantArgumentForNode(currentNode, munchingState);
- if (currentConstantArgument == null || !currentNode.hasSinglePredecessor()) {
- return false;
- }
- StringBuilderNode previous = currentNode.getSinglePredecessor();
- String previousConstantArgument = getConstantArgumentForNode(previous, munchingState);
- if (previousConstantArgument == null || !previous.hasSingleSuccessor()) {
+ InitOrAppendNode previous = currentNode.getSinglePredecessor().asInitOrAppend();
+ if (previous == null || !previous.hasSingleSuccessor()) {
return false;
}
// The capacity changes based on the init call (on JVM it adds 16 to length of input).
if (previous.isInitNode() && munchingState.inspectingCapacity.contains(root)) {
return false;
}
- assert previous.isInitOrAppend();
+ String currentConstantArgument = getConstantArgumentForNode(appendNode, munchingState);
+ if (currentConstantArgument == null) {
+ return false;
+ }
+ String previousConstantArgument = getConstantArgumentForNode(previous, munchingState);
+ if (previousConstantArgument == null) {
+ return false;
+ }
String newConstant = previousConstantArgument + currentConstantArgument;
- InitOrAppend initOrAppend = previous.asInitOrAppend();
- initOrAppend.setConstantArgument(newConstant);
+ previous.setConstantArgument(newConstant);
munchingState.actions.put(
- initOrAppend.getInstruction(), new AppendWithNewConstantString(newConstant));
+ previous.getInstruction(), new AppendWithNewConstantString(newConstant));
munchingState.actions.put(
- currentNode.asAppendNode().getInstruction(), RemoveStringBuilderAction.getInstance());
+ appendNode.getInstruction(), RemoveStringBuilderAction.getInstance());
currentNode.removeNode();
return true;
}
@@ -126,13 +150,13 @@
* This peephole will try to remove toString nodes and replace by a constant string:
*
* <pre>
- * newInstance -> init("foo") -> append("bar") -> toString() =>
- * newInstance -> init("foo") -> append("bar")
+ * newInstance -> init("foo") -> toString() => newInstance -> init("foo") -> append("bar")
+ * actions: [toString() => ReplaceByConstantString("foo")]
* </pre>
*
* <p>If the node is an implicitToString, we update the append of another builder to have the new
- * constant value directly. If not, we keep track of the outValue toString() had is replaced by a
- * constant, by updating {@code MunchingState.optimizedStrings}
+ * constant value directly. If not, we keep track of the outValue toString() being replaced by a
+ * constant by updating {@code MunchingState.optimizedStrings}
*/
private static class MunchToString implements PeepholePattern {
@@ -141,6 +165,9 @@
StringBuilderNode originalRoot,
StringBuilderNode currentNode,
MunchingState munchingState) {
+ if (munchingState.isEscaping(originalRoot) || munchingState.isInspecting(originalRoot)) {
+ return false;
+ }
if (!currentNode.isToStringNode() && !currentNode.isImplicitToStringNode()) {
return false;
}
@@ -148,48 +175,163 @@
if (newInstanceNode == null || !newInstanceNode.hasSingleSuccessor()) {
return false;
}
- StringBuilderNode init = newInstanceNode.getSingleSuccessor();
- String rootConstantArgument = getConstantArgumentForNode(init, munchingState);
- if (rootConstantArgument == null || !init.isInitNode()) {
+ InitNode init = newInstanceNode.getSingleSuccessor().asInitNode();
+ if (init == null || !init.hasSingleSuccessor()) {
return false;
}
- // This is either <init>(str) -> toString() or <init>(str) -> append(str) -> toString()
+ if (!currentNode.hasSinglePredecessor() || currentNode.getSinglePredecessor() != init) {
+ return false;
+ }
+ String initConstantArgument = getConstantArgumentForNode(init, munchingState);
+ if (initConstantArgument == null) {
+ return false;
+ }
// If the string builder dependency is directly given to another string builder, there is
// no toString() but an append with this string builder as argument.
- if (!currentNode.hasSinglePredecessor() || !init.hasSingleSuccessor()) {
- return false;
- }
- String constantArgument = null;
- if (currentNode.getSinglePredecessor() == init) {
- constantArgument = rootConstantArgument;
- } else {
- StringBuilderNode expectedAppend = init.getSingleSuccessor();
- StringBuilderNode expectedSameAppend = currentNode.getSinglePredecessor();
- String appendConstantArgument = getConstantArgumentForNode(expectedAppend, munchingState);
- if (expectedAppend == expectedSameAppend && appendConstantArgument != null) {
- // TODO(b/190489514): See if this larger pattern is necessary.
- assert false : "See why this larger pattern is necessary";
- constantArgument = rootConstantArgument + appendConstantArgument;
- }
- }
- if (constantArgument == null) {
- return false;
- }
if (currentNode.isToStringNode()) {
ToStringNode toStringNode = currentNode.asToStringNode();
munchingState.actions.put(
- toStringNode.getInstruction(), new ReplaceByConstantString(constantArgument));
+ toStringNode.getInstruction(), new ReplaceByConstantString(initConstantArgument));
String oldValue =
munchingState.optimizedStrings.put(
- toStringNode.getInstruction().outValue(), constantArgument);
+ toStringNode.getInstruction().outValue(), initConstantArgument);
assert oldValue == null;
} else {
assert currentNode.isImplicitToStringNode();
ImplicitToStringNode implicitToStringNode = currentNode.asImplicitToStringNode();
- InitOrAppend initOrAppend = implicitToStringNode.getInitOrAppend();
- initOrAppend.setConstantArgument(constantArgument);
+ InitOrAppendNode initOrAppend = implicitToStringNode.getInitOrAppend();
+ initOrAppend.setConstantArgument(initConstantArgument);
munchingState.actions.put(
- initOrAppend.getInstruction(), new AppendWithNewConstantString(constantArgument));
+ initOrAppend.getInstruction(), new AppendWithNewConstantString(initConstantArgument));
+ }
+ munchingState.materializingInstructions.get(originalRoot).remove(currentNode);
+ currentNode.removeNode();
+ return true;
+ }
+ }
+
+ /**
+ * This peephole will try to remove toString nodes and replace by an invoke to String.concat:
+ *
+ * <pre>
+ * newInstance -> init(notNull(string)) -> append(notNull(otherString)) -> toString() =>
+ * newInstance -> init(notNull(string)) -> append(otherString)
+ * actions: [toString() => string.concat(otherString)]
+ * </pre>
+ *
+ * <p>This pattern only triggers when a constant munching of toString could happen.
+ */
+ private static class MunchToStringIntoStringConcat implements PeepholePattern {
+
+ @Override
+ public boolean optimize(
+ StringBuilderNode originalRoot,
+ StringBuilderNode currentNode,
+ MunchingState munchingState) {
+ if (munchingState.isEscaping(originalRoot)
+ || munchingState.isInspecting(originalRoot)
+ || !currentNode.hasSinglePredecessor()) {
+ return false;
+ }
+ if (!currentNode.isToStringNode() && !currentNode.isImplicitToStringNode()) {
+ return false;
+ }
+ NewInstanceNode newInstanceNode = munchingState.getNewInstanceNode(originalRoot);
+ if (newInstanceNode == null || !newInstanceNode.hasSingleSuccessor()) {
+ return false;
+ }
+ InitOrAppendNode firstNode = newInstanceNode.getSingleSuccessor().asInitNode();
+ if (firstNode == null || !firstNode.hasSingleSuccessor()) {
+ return false;
+ }
+ if (firstNode.asInitNode().isConstructorInvokeSideEffectFree(munchingState.oracle)
+ && "".equals(firstNode.getConstantArgument())
+ && firstNode.hasSingleSuccessor()) {
+ firstNode = firstNode.getSingleSuccessor().asAppendNode();
+ if (firstNode == null
+ || !firstNode.hasSinglePredecessor()
+ || !firstNode.hasSingleSuccessor()) {
+ return false;
+ }
+ }
+ // We cannot String.concat or return the string safely when it is not constant and maybe null.
+ if (!firstNode.hasConstantOrNonConstantArgument()) {
+ return false;
+ }
+ List<InitOrAppendNode> initOrAppends = Lists.newArrayList(firstNode);
+ if (currentNode.getSinglePredecessor() != firstNode) {
+ AppendNode appendAfterFirstNode = firstNode.getSingleSuccessor().asAppendNode();
+ AppendNode appendBeforeToString = currentNode.getSinglePredecessor().asAppendNode();
+ if (appendAfterFirstNode == null
+ || appendAfterFirstNode != appendBeforeToString
+ || !appendAfterFirstNode.hasConstantOrNonConstantArgument()) {
+ return false;
+ }
+ initOrAppends.add(appendAfterFirstNode);
+ }
+ // Check that all values are not constant otherwise we can compute the constant value and
+ // replace all entirely.
+ if (Iterables.all(initOrAppends, InitOrAppendNode::hasConstantArgument)) {
+ return false;
+ }
+ InitOrAppendNode first = initOrAppends.get(0);
+ // If the string builder dependency is directly given to another string builder, there is
+ // no toString() but an append with this string builder as argument.
+ if (currentNode.isToStringNode()) {
+ Instruction currentInstruction = currentNode.asToStringNode().getInstruction();
+ if (initOrAppends.size() == 1) {
+ // Replace with the string itself.
+ munchingState.actions.put(
+ currentInstruction, new ReplaceByExistingString(first.getNonConstantArgument()));
+ } else {
+ InitOrAppendNode second = initOrAppends.get(1);
+ ReplaceByStringConcat concatAction;
+ if (first.hasConstantArgument()) {
+ concatAction =
+ ReplaceByStringConcat.replaceByNewConstantConcatValue(
+ first.getConstantArgument(), second.getNonConstantArgument());
+ } else if (second.hasConstantArgument()) {
+ concatAction =
+ ReplaceByStringConcat.replaceByValueConcatNewConstant(
+ first.getNonConstantArgument(), second.getConstantArgument());
+ } else {
+ concatAction =
+ ReplaceByStringConcat.replaceByValues(
+ first.getNonConstantArgument(), second.getNonConstantArgument());
+ }
+ munchingState.actions.put(currentInstruction, concatAction);
+ }
+ } else {
+ assert currentNode.isImplicitToStringNode();
+ ImplicitToStringNode implicitToStringNode = currentNode.asImplicitToStringNode();
+ InitOrAppendNode initOrAppend = implicitToStringNode.getInitOrAppend();
+ if (initOrAppends.size() == 1) {
+ initOrAppend.setNonConstantArgument(first.getNonConstantArgument());
+ munchingState.actions.put(
+ initOrAppend.getInstruction(),
+ new ReplaceArgumentByExistingString(first.getNonConstantArgument()));
+ } else {
+ // Changing append to String.concat require us to calculate a new string value that will
+ // be the result. We allocate it here such that we can use it repeatedly in munching.
+ InitOrAppendNode second = initOrAppends.get(1);
+ Value newOutValue = munchingState.getNewOutValue();
+ initOrAppend.setNonConstantArgument(newOutValue);
+ ReplaceArgumentByStringConcat concatAction;
+ if (first.hasConstantArgument()) {
+ concatAction =
+ ReplaceArgumentByStringConcat.replaceByNewConstantConcatValue(
+ first.getConstantArgument(), second.getNonConstantArgument(), newOutValue);
+ } else if (second.hasConstantArgument()) {
+ concatAction =
+ ReplaceArgumentByStringConcat.replaceByValueConcatNewConstant(
+ first.getNonConstantArgument(), second.getConstantArgument(), newOutValue);
+ } else {
+ concatAction =
+ ReplaceArgumentByStringConcat.replaceByValues(
+ first.getNonConstantArgument(), second.getNonConstantArgument(), newOutValue);
+ }
+ munchingState.actions.put(initOrAppend.getInstruction(), concatAction);
+ }
}
munchingState.materializingInstructions.get(originalRoot).remove(currentNode);
currentNode.removeNode();
@@ -198,21 +340,11 @@
}
private static String getConstantArgumentForNode(
- StringBuilderNode node, MunchingState munchingState) {
- if (node.isAppendNode()) {
- AppendNode appendNode = node.asAppendNode();
- if (appendNode.hasConstantArgument()) {
- return appendNode.getConstantArgument();
- }
- return getOptimizedConstantArgument(appendNode, munchingState);
- } else if (node.isInitNode()) {
- InitNode initNode = node.asInitNode();
- if (initNode.hasConstantArgument()) {
- return initNode.getConstantArgument();
- }
- return getOptimizedConstantArgument(initNode, munchingState);
+ InitOrAppendNode node, MunchingState munchingState) {
+ if (node.hasConstantArgument()) {
+ return node.getConstantArgument();
}
- return null;
+ return getOptimizedConstantArgument(node, munchingState);
}
private static String getOptimizedConstantArgument(
@@ -253,16 +385,16 @@
boolean canRemoveIfLastAndNoLoop =
!isLoopingOnPath(root, currentNode, munchingState)
&& currentNode.getSuccessors().isEmpty();
+ boolean hasKnownArgumentOrCannotBeObserved =
+ appendNode.hasConstantOrNonConstantArgument()
+ || !munchingState.oracle.canObserveStringBuilderCall(
+ currentNode.asAppendNode().getInstruction());
if (canRemoveIfNoInspectionOrMaterializing
&& canRemoveIfLastAndNoLoop
- && !munchingState.oracle.canObserveStringBuilderCall(
- currentNode.asAppendNode().getInstruction())) {
- munchingState.actions.put(
- appendNode.getInstruction(), RemoveStringBuilderAction.getInstance());
+ && hasKnownArgumentOrCannotBeObserved) {
removeNode = true;
}
} else if (currentNode.isInitNode()
- && currentNode.asInitNode().hasConstantArgument()
&& currentNode.hasSinglePredecessor()
&& currentNode.getSinglePredecessor().isNewInstanceNode()
&& currentNode.getSuccessors().isEmpty()
@@ -277,9 +409,17 @@
removedAnyNodes = true;
currentNode.removeNode();
if (currentNode.isStringBuilderInstructionNode()) {
- munchingState.actions.put(
- currentNode.asStringBuilderInstructionNode().getInstruction(),
- RemoveStringBuilderAction.getInstance());
+ Instruction currentInstruction =
+ currentNode.asStringBuilderInstructionNode().getInstruction();
+ StringBuilderAction currentAction = munchingState.actions.get(currentInstruction);
+ if (currentAction != null
+ && !currentAction.isAllowedToBeOverwrittenByRemoveStringBuilderAction()) {
+ assert currentAction.isReplaceArgumentByStringConcat();
+ currentAction.asReplaceArgumentByStringConcat().setRemoveInstruction();
+ } else {
+ munchingState.actions.put(
+ currentInstruction, RemoveStringBuilderAction.getInstance());
+ }
}
currentNode =
currentNode.hasSinglePredecessor() ? currentNode.getSinglePredecessor() : null;
@@ -312,7 +452,12 @@
}
private static final PeepholePattern[] peepholePatterns =
- new PeepholePattern[] {new MunchAppends(), new MunchToString(), new MunchNonMaterializing()};
+ new PeepholePattern[] {
+ new MunchAppends(),
+ new MunchToString(),
+ new MunchToStringIntoStringConcat(),
+ new MunchNonMaterializing()
+ };
static boolean optimize(
StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
index 063f791..8b59c2f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
@@ -43,6 +43,12 @@
boolean isInit(Instruction instruction);
+ boolean isAppendString(Instruction instruction);
+
+ boolean isStringConstructor(Instruction instruction);
+
+ boolean isConstructorInvokeSideEffectFree(Instruction instruction);
+
class DefaultStringBuilderOracle implements StringBuilderOracle {
private final DexItemFactory factory;
@@ -185,12 +191,12 @@
}
DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
if (factory.stringBuilderMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
- || factory.stringBufferMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
- || factory.stringBuilderMethods.charSequenceConstructor == invokedMethod
- || factory.stringBufferMethods.charSequenceConstructor == invokedMethod) {
- assert instruction.inValues().size() == 2;
+ || factory.stringBufferMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)) {
return !instruction.inValues().get(1).getType().isStringType(factory);
}
+ if (invokedMethod.isInstanceInitializer(factory)) {
+ return !isConstructorInvokeSideEffectFree(instruction);
+ }
if (factory.stringBuilderMethods.isAppendCharArrayMethod(invokedMethod)
|| factory.stringBufferMethods.isAppendCharArrayMethod(invokedMethod)) {
return instruction.asInvokeVirtual().getFirstNonReceiverArgument().isMaybeNull();
@@ -207,5 +213,41 @@
return factory.stringBuilderMethods.isConstructorMethod(invokedMethod)
|| factory.stringBufferMethods.isConstructorMethod(invokedMethod);
}
+
+ @Override
+ public boolean isAppendString(Instruction instruction) {
+ if (!instruction.isInvokeMethod()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ return factory.stringBuilderMethods.isAppendStringMethod(invokedMethod)
+ || factory.stringBufferMethods.isAppendStringMethod(invokedMethod);
+ }
+
+ @Override
+ public boolean isStringConstructor(Instruction instruction) {
+ if (!instruction.isInvokeMethod()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ return invokedMethod == factory.stringBuilderMethods.stringConstructor
+ || invokedMethod == factory.stringBufferMethods.stringConstructor;
+ }
+
+ @Override
+ public boolean isConstructorInvokeSideEffectFree(Instruction instruction) {
+ if (!instruction.isInvokeConstructor(factory)) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ if (invokedMethod.getHolderType() == factory.stringBuilderType) {
+ return factory.stringBuilderMethods.constructorInvokeIsSideEffectFree(
+ invokedMethod, instruction.inValues());
+ } else {
+ assert invokedMethod.getHolderType() == factory.stringBufferType;
+ return factory.stringBufferMethods.constructorInvokeIsSideEffectFree(
+ invokedMethod, instruction.inValues());
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/EqualsCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/EqualsCfCodeProvider.java
index d1a4bf3..febbc58 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/EqualsCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/EqualsCfCodeProvider.java
@@ -43,15 +43,23 @@
public CfCode generateCfCode() {
// return wrapperField.equals(
// other instanceof WrapperType ? ((WrapperType) other).wrapperField : other);
+ DexType wrapperType = wrapperField.getHolderType();
+ FrameType[] locals = {
+ FrameType.initialized(wrapperType), FrameType.initialized(appView.dexItemFactory().objectType)
+ };
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
+ // this.wrapperField
List<CfInstruction> instructions = new ArrayList<>();
instructions.add(new CfLoad(ValueType.OBJECT, 0));
instructions.add(new CfInstanceFieldRead(wrapperField));
+
+ // other instanceof WrapperType
instructions.add(new CfLoad(ValueType.OBJECT, 1));
- DexType wrapperType = wrapperField.getHolderType();
instructions.add(new CfInstanceOf(wrapperType));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, label1));
+
+ // ((WrapperType) other).wrapperField
instructions.add(new CfLoad(ValueType.OBJECT, 1));
instructions.add(new CfCheckCast(wrapperType));
instructions.add(new CfInstanceFieldRead(wrapperField));
@@ -59,27 +67,21 @@
instructions.add(label1);
instructions.add(
new CfFrame(
- new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
- new FrameType[] {
- FrameType.initialized(wrapperType),
- FrameType.initialized(appView.dexItemFactory().objectType)
- }),
- new ArrayDeque<>(Arrays.asList(FrameType.initialized(wrapperType)))));
+ new Int2ObjectAVLTreeMap<>(new int[] {0, 1}, locals),
+ new ArrayDeque<>(Arrays.asList(FrameType.initialized(wrapperField.type)))));
+
+ // other
instructions.add(new CfLoad(ValueType.OBJECT, 1));
instructions.add(label2);
instructions.add(
new CfFrame(
- new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
- new FrameType[] {
- FrameType.initialized(wrapperType),
- FrameType.initialized(appView.dexItemFactory().objectType)
- }),
+ new Int2ObjectAVLTreeMap<>(new int[] {0, 1}, locals),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(wrapperType),
+ FrameType.initialized(wrapperField.type),
FrameType.initialized(appView.dexItemFactory().objectType)))));
+
+ // equals.
instructions.add(
new CfInvoke(Opcodes.INVOKEVIRTUAL, appView.dexItemFactory().objectMembers.equals, false));
instructions.add(new CfReturn(ValueType.INT));
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
index a32d51e..8f0f2f4 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
@@ -193,14 +193,19 @@
public static class WrapperConversionCfCodeProvider extends NullableConversionCfCodeProvider {
- DexField reverseWrapperField;
- DexField wrapperField;
+ private final DexField reverseWrapperField;
+ private final DexField wrapperField;
+ private final List<DexMethod> subwrapperConvertList;
public WrapperConversionCfCodeProvider(
- AppView<?> appView, DexField reverseWrapperField, DexField wrapperField) {
+ AppView<?> appView,
+ DexField reverseWrapperField,
+ DexField wrapperField,
+ List<DexMethod> subwrapperConvertList) {
super(appView, wrapperField.holder);
this.reverseWrapperField = reverseWrapperField;
this.wrapperField = wrapperField;
+ this.subwrapperConvertList = subwrapperConvertList;
}
@Override
@@ -228,6 +233,23 @@
instructions.add(unwrapDest);
instructions.add(frame.clone());
+ // if (arg instanceOf Subtype) {
+ // return SubtypeWrapper.convert((Subtype) arg)
+ // };
+ for (DexMethod convert : subwrapperConvertList) {
+ CfLabel dest = new CfLabel();
+ DexType convertArgType = convert.getArgumentType(0, true);
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfInstanceOf(convertArgType));
+ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest));
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfCheckCast(convertArgType));
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, convert, false));
+ instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
+ instructions.add(dest);
+ instructions.add(frame.clone());
+ }
+
// return new Wrapper(wrappedValue);
instructions.add(new CfNew(wrapperField.holder));
instructions.add(CfStackInstruction.fromAsm(Opcodes.DUP));
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index 973a89d..9a74b4e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -159,7 +159,7 @@
// TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
private static final String javaIdentifierSegment =
- "\\p{javaJavaIdentifierStart}(?:-|\\p{javaJavaIdentifierPart})*";
+ "\\p{javaJavaIdentifierStart}[-\\p{javaJavaIdentifierPart}]*";
private static final String METHOD_NAME_REGULAR_EXPRESSION =
"(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
index cbbfc33..1c5dd2b 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
@@ -38,14 +38,13 @@
public final class InstrumentationServerFactory {
public static DexProgramClass createClass(DexItemFactory dexItemFactory) {
return new DexProgramClass(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/generated/InstrumentationServerFactory;"),
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;"),
Kind.CF,
Origin.unknown(),
- ClassAccessFlags.fromCfAccessFlags(1025),
- null,
+ ClassAccessFlags.fromCfAccessFlags(1057),
+ dexItemFactory.createType("Ljava/lang/Object;"),
DexTypeList.empty(),
- dexItemFactory.createString("InstrumentationServerFactory"),
+ dexItemFactory.createString("InstrumentationServer.java"),
NestHostClassAttribute.none(),
Collections.emptyList(),
Collections.emptyList(),
@@ -72,6 +71,18 @@
private static DexEncodedMethod[] createDirectMethods(DexItemFactory dexItemFactory) {
return new DexEncodedMethod[] {
DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(1, true))
+ .setApiLevelForCode(ComputedApiLevel.unknown())
+ .setApiLevelForDefinition(ComputedApiLevel.unknown())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setMethod(
+ dexItemFactory.createMethod(
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;"),
+ dexItemFactory.createProto(dexItemFactory.createType("V")),
+ dexItemFactory.createString("<init>")))
+ .setCode(method -> createInstanceInitializerCfCode0(dexItemFactory, method))
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
@@ -91,17 +102,6 @@
private static DexEncodedMethod[] createVirtualMethods(DexItemFactory dexItemFactory) {
return new DexEncodedMethod[] {
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(1, false))
- .setApiLevelForCode(ComputedApiLevel.unknown())
- .setApiLevelForDefinition(ComputedApiLevel.unknown())
- .setClassFileVersion(CfVersion.V1_8)
- .setMethod(
- dexItemFactory.createInstanceInitializer(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServer;")))
- .setCode(method -> createInstanceInitializerCfCode0(dexItemFactory, method))
- .build(),
- DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(1025, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
index f0c8f9b..e5af39a 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
@@ -11,15 +11,18 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfGoto;
+import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfInstanceFieldRead;
import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfMonitor;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfReturnVoid;
@@ -45,6 +48,8 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Monitor;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.origin.Origin;
import com.google.common.collect.ImmutableList;
@@ -56,14 +61,13 @@
public final class InstrumentationServerImplFactory {
public static DexProgramClass createClass(DexItemFactory dexItemFactory) {
return new DexProgramClass(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/generated/InstrumentationServerImplFactory;"),
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
Kind.CF,
Origin.unknown(),
- ClassAccessFlags.fromCfAccessFlags(1),
- null,
+ ClassAccessFlags.fromCfAccessFlags(33),
+ dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;"),
DexTypeList.empty(),
- dexItemFactory.createString("InstrumentationServerImplFactory"),
+ dexItemFactory.createString("InstrumentationServerImpl.java"),
NestHostClassAttribute.none(),
Collections.emptyList(),
Collections.emptyList(),
@@ -86,28 +90,8 @@
dexItemFactory.createField(
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- dexItemFactory.createType("Ljava/lang/StringBuilder;"),
- dexItemFactory.createString("builder")))
- .setAccessFlags(FieldAccessFlags.fromCfAccessFlags(18))
- .setApiLevel(ComputedApiLevel.unknown())
- .build(),
- DexEncodedField.syntheticBuilder()
- .setField(
- dexItemFactory.createField(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- dexItemFactory.createType("Ljava/lang/String;"),
- dexItemFactory.createString("logcatTag")))
- .setAccessFlags(FieldAccessFlags.fromCfAccessFlags(18))
- .setApiLevel(ComputedApiLevel.unknown())
- .build(),
- DexEncodedField.syntheticBuilder()
- .setField(
- dexItemFactory.createField(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- dexItemFactory.createType("Z"),
- dexItemFactory.createString("writeToLogcat")))
+ dexItemFactory.createType("Ljava/util/LinkedHashSet;"),
+ dexItemFactory.createString("lines")))
.setAccessFlags(FieldAccessFlags.fromCfAccessFlags(18))
.setApiLevel(ComputedApiLevel.unknown())
.build()
@@ -126,6 +110,26 @@
dexItemFactory.createString("INSTANCE")))
.setAccessFlags(FieldAccessFlags.fromCfAccessFlags(26))
.setApiLevel(ComputedApiLevel.unknown())
+ .build(),
+ DexEncodedField.syntheticBuilder()
+ .setField(
+ dexItemFactory.createField(
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createType("Z"),
+ dexItemFactory.createString("writeToLogcat")))
+ .setAccessFlags(FieldAccessFlags.fromCfAccessFlags(10))
+ .setApiLevel(ComputedApiLevel.unknown())
+ .build(),
+ DexEncodedField.syntheticBuilder()
+ .setField(
+ dexItemFactory.createField(
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createType("Ljava/lang/String;"),
+ dexItemFactory.createString("logcatTag")))
+ .setAccessFlags(FieldAccessFlags.fromCfAccessFlags(10))
+ .setApiLevel(ComputedApiLevel.unknown())
.build()
};
}
@@ -133,18 +137,20 @@
private static DexEncodedMethod[] createDirectMethods(DexItemFactory dexItemFactory) {
return new DexEncodedMethod[] {
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, true))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
.setClassFileVersion(CfVersion.V1_8)
.setMethod(
- dexItemFactory.createInstanceInitializer(
+ dexItemFactory.createMethod(
dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")))
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createProto(dexItemFactory.createType("V")),
+ dexItemFactory.createString("<init>")))
.setCode(method -> createInstanceInitializerCfCode1(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(34, false))
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
.setClassFileVersion(CfVersion.V1_8)
@@ -153,10 +159,10 @@
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
dexItemFactory.createProto(
- dexItemFactory.createType("V"),
- dexItemFactory.createType("Ljava/lang/String;")),
- dexItemFactory.createString("addLine")))
- .setCode(method -> createCfCode2_addLine(dexItemFactory, method))
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ dexItemFactory.createString("getInstance")))
+ .setCode(method -> createCfCode5_getInstance(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
@@ -189,7 +195,7 @@
.setCode(method -> createCfCode4_addSyntheticMethod(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
.setClassFileVersion(CfVersion.V1_8)
@@ -198,10 +204,10 @@
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
dexItemFactory.createProto(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
- dexItemFactory.createString("getInstance")))
- .setCode(method -> createCfCode5_getInstance(dexItemFactory, method))
+ dexItemFactory.createType("V"),
+ dexItemFactory.createType("Ljava/lang/String;")),
+ dexItemFactory.createString("addLine")))
+ .setCode(method -> createCfCode2_addLine(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
@@ -217,6 +223,19 @@
dexItemFactory.createType("Ljava/lang/String;")),
dexItemFactory.createString("writeToLogcat")))
.setCode(method -> createCfCode7_writeToLogcat(dexItemFactory, method))
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(8, true))
+ .setApiLevelForCode(ComputedApiLevel.unknown())
+ .setApiLevelForDefinition(ComputedApiLevel.unknown())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setMethod(
+ dexItemFactory.createMethod(
+ dexItemFactory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ dexItemFactory.createProto(dexItemFactory.createType("V")),
+ dexItemFactory.createString("<clinit>")))
+ .setCode(method -> createClassInitializerCfCode(dexItemFactory, method))
.build()
};
}
@@ -224,7 +243,7 @@
private static DexEncodedMethod[] createVirtualMethods(DexItemFactory dexItemFactory) {
return new DexEncodedMethod[] {
DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(33, false))
+ .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(1, false))
.setApiLevelForCode(ComputedApiLevel.unknown())
.setApiLevelForDefinition(ComputedApiLevel.unknown())
.setClassFileVersion(CfVersion.V1_8)
@@ -273,8 +292,6 @@
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
CfLabel label3 = new CfLabel();
- CfLabel label4 = new CfLabel();
- CfLabel label5 = new CfLabel();
return new CfCode(
method.holder,
3,
@@ -291,39 +308,23 @@
false),
label1,
new CfLoad(ValueType.OBJECT, 0),
- new CfNew(factory.stringBuilderType),
+ new CfNew(factory.createType("Ljava/util/LinkedHashSet;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfInvoke(
183,
factory.createMethod(
- factory.stringBuilderType,
+ factory.createType("Ljava/util/LinkedHashSet;"),
factory.createProto(factory.voidType),
factory.createString("<init>")),
false),
new CfInstanceFieldWrite(
factory.createField(
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.stringBuilderType,
- factory.createString("builder"))),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createString("lines"))),
label2,
- new CfLoad(ValueType.OBJECT, 0),
- new CfConstNumber(0, ValueType.INT),
- new CfInstanceFieldWrite(
- factory.createField(
- factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.booleanType,
- factory.createString("writeToLogcat"))),
- label3,
- new CfLoad(ValueType.OBJECT, 0),
- new CfConstString(factory.createString("r8")),
- new CfInstanceFieldWrite(
- factory.createField(
- factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.stringType,
- factory.createString("logcatTag"))),
- label4,
new CfReturnVoid(),
- label5),
+ label3),
ImmutableList.of(),
ImmutableList.of());
}
@@ -332,39 +333,130 @@
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ CfLabel label10 = new CfLabel();
+ CfLabel label11 = new CfLabel();
return new CfCode(
method.holder,
2,
- 2,
+ 4,
ImmutableList.of(
label0,
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.stringBuilderType,
- factory.createString("builder"))),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createString("lines"))),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfStore(ValueType.OBJECT, 2),
+ new CfMonitor(Monitor.Type.ENTER),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createString("lines"))),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
182,
factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringBuilderType, factory.stringType),
- factory.createString("append")),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createProto(factory.booleanType, factory.objectType),
+ factory.createString("add")),
false),
- new CfConstNumber(10, ValueType.INT),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringBuilderType, factory.charType),
- factory.createString("append")),
- false),
- new CfStackInstruction(CfStackInstruction.Opcode.Pop),
- label1,
+ new CfIf(If.Type.NE, ValueType.INT, label4),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfMonitor(Monitor.Type.EXIT),
+ label3,
new CfReturnVoid(),
- label2),
- ImmutableList.of(),
+ label4,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.stringType),
+ FrameType.initializedNonNullReference(factory.objectType)
+ })),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfMonitor(Monitor.Type.EXIT),
+ label5,
+ new CfGoto(label8),
+ label6,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.stringType),
+ FrameType.initializedNonNullReference(factory.objectType)
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initializedNonNullReference(factory.throwableType)))),
+ new CfStore(ValueType.OBJECT, 3),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfMonitor(Monitor.Type.EXIT),
+ label7,
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfThrow(),
+ label8,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.stringType)
+ })),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.booleanType,
+ factory.createString("writeToLogcat"))),
+ new CfIf(If.Type.EQ, ValueType.INT, label10),
+ label9,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("writeToLogcat")),
+ false),
+ label10,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.stringType)
+ })),
+ new CfReturnVoid(),
+ label11),
+ ImmutableList.of(
+ new CfTryCatch(
+ label1, label3, ImmutableList.of(factory.throwableType), ImmutableList.of(label6)),
+ new CfTryCatch(
+ label4, label5, ImmutableList.of(factory.throwableType), ImmutableList.of(label6)),
+ new CfTryCatch(
+ label6, label7, ImmutableList.of(factory.throwableType), ImmutableList.of(label6))),
ImmutableList.of());
}
@@ -495,102 +587,146 @@
CfLabel label5 = new CfLabel();
CfLabel label6 = new CfLabel();
CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ CfLabel label10 = new CfLabel();
+ CfLabel label11 = new CfLabel();
+ CfLabel label12 = new CfLabel();
+ CfLabel label13 = new CfLabel();
+ CfLabel label14 = new CfLabel();
+ CfLabel label15 = new CfLabel();
+ CfLabel label16 = new CfLabel();
return new CfCode(
method.holder,
- 3,
4,
+ 8,
ImmutableList.of(
label0,
- new CfNew(factory.createType("Ljava/io/FileOutputStream;")),
+ new CfNew(factory.createType("Ljava/io/PrintWriter;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 1),
+ new CfConstString(factory.createString("UTF-8")),
new CfInvoke(
183,
factory.createMethod(
- factory.createType("Ljava/io/FileOutputStream;"),
- factory.createProto(factory.voidType, factory.createType("Ljava/io/File;")),
+ factory.createType("Ljava/io/PrintWriter;"),
+ factory.createProto(
+ factory.voidType, factory.createType("Ljava/io/File;"), factory.stringType),
factory.createString("<init>")),
false),
new CfStore(ValueType.OBJECT, 2),
label1,
- new CfLoad(ValueType.OBJECT, 2),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.stringBuilderType,
- factory.createString("builder"))),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringType),
- factory.createString("toString")),
- false),
- new CfConstString(factory.createString("UTF-8")),
- new CfInvoke(
- 184,
- factory.createMethod(
- factory.createType("Ljava/nio/charset/Charset;"),
- factory.createProto(
- factory.createType("Ljava/nio/charset/Charset;"), factory.stringType),
- factory.createString("forName")),
- false),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringType,
- factory.createProto(
- factory.byteArrayType, factory.createType("Ljava/nio/charset/Charset;")),
- factory.createString("getBytes")),
- false),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.createType("Ljava/io/FileOutputStream;"),
- factory.createProto(factory.voidType, factory.byteArrayType),
- factory.createString("write")),
- false),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createString("lines"))),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfStore(ValueType.OBJECT, 3),
+ new CfMonitor(Monitor.Type.ENTER),
label2,
- new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createString("lines"))),
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Ljava/io/FileOutputStream;"),
- factory.createProto(factory.voidType),
- factory.createString("close")),
+ factory.createType("Ljava/util/LinkedHashSet;"),
+ factory.createProto(factory.createType("Ljava/util/Iterator;")),
+ factory.createString("iterator")),
false),
+ new CfStore(ValueType.OBJECT, 4),
label3,
- new CfGoto(label6),
- label4,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1, 2},
+ new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
FrameType.initializedNonNullReference(
- factory.createType("Ljava/io/FileOutputStream;"))
- }),
- new ArrayDeque<>(
- Arrays.asList(FrameType.initializedNonNullReference(factory.throwableType)))),
- new CfStore(ValueType.OBJECT, 3),
+ factory.createType("Ljava/io/PrintWriter;")),
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/Iterator;"))
+ })),
+ new CfLoad(ValueType.OBJECT, 4),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/Iterator;"),
+ factory.createProto(factory.booleanType),
+ factory.createString("hasNext")),
+ true),
+ new CfIf(If.Type.EQ, ValueType.INT, label6),
+ new CfLoad(ValueType.OBJECT, 4),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/Iterator;"),
+ factory.createProto(factory.objectType),
+ factory.createString("next")),
+ true),
+ new CfCheckCast(factory.stringType),
+ new CfStore(ValueType.OBJECT, 5),
+ label4,
new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 5),
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Ljava/io/FileOutputStream;"),
- factory.createProto(factory.voidType),
- factory.createString("close")),
+ factory.createType("Ljava/io/PrintWriter;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("println")),
false),
label5,
- new CfLoad(ValueType.OBJECT, 3),
- new CfThrow(),
+ new CfGoto(label3),
label6,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/io/PrintWriter;")),
+ FrameType.initializedNonNullReference(factory.objectType)
+ })),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfMonitor(Monitor.Type.EXIT),
+ label7,
+ new CfGoto(label10),
+ label8,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/io/PrintWriter;")),
+ FrameType.initializedNonNullReference(factory.objectType)
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initializedNonNullReference(factory.throwableType)))),
+ new CfStore(ValueType.OBJECT, 6),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfMonitor(Monitor.Type.EXIT),
+ label9,
+ new CfLoad(ValueType.OBJECT, 6),
+ new CfThrow(),
+ label10,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
@@ -598,13 +734,74 @@
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
FrameType.initializedNonNullReference(
- factory.createType("Ljava/io/FileOutputStream;"))
+ factory.createType("Ljava/io/PrintWriter;"))
+ })),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/io/PrintWriter;"),
+ factory.createProto(factory.voidType),
+ factory.createString("close")),
+ false),
+ label11,
+ new CfGoto(label15),
+ label12,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/io/PrintWriter;"))
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(FrameType.initializedNonNullReference(factory.throwableType)))),
+ new CfStore(ValueType.OBJECT, 7),
+ label13,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/io/PrintWriter;"),
+ factory.createProto(factory.voidType),
+ factory.createString("close")),
+ false),
+ label14,
+ new CfLoad(ValueType.OBJECT, 7),
+ new CfThrow(),
+ label15,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType(
+ "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
+ FrameType.initializedNonNullReference(factory.createType("Ljava/io/File;")),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/io/PrintWriter;"))
})),
new CfReturnVoid(),
- label7),
+ label16),
ImmutableList.of(
new CfTryCatch(
- label1, label2, ImmutableList.of(factory.throwableType), ImmutableList.of(label4))),
+ label2, label7, ImmutableList.of(factory.throwableType), ImmutableList.of(label8)),
+ new CfTryCatch(
+ label8, label9, ImmutableList.of(factory.throwableType), ImmutableList.of(label8)),
+ new CfTryCatch(
+ label1,
+ label10,
+ ImmutableList.of(factory.throwableType),
+ ImmutableList.of(label12)),
+ new CfTryCatch(
+ label12,
+ label13,
+ ImmutableList.of(factory.throwableType),
+ ImmutableList.of(label12))),
ImmutableList.of());
}
@@ -618,14 +815,18 @@
2,
ImmutableList.of(
label0,
- new CfConstString(factory.createString("r8")),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
+ factory.stringType,
+ factory.createString("logcatTag"))),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
184,
factory.createMethod(
factory.createType("Landroid/util/Log;"),
factory.createProto(factory.intType, factory.stringType, factory.stringType),
- factory.createString("v")),
+ factory.createString("i")),
false),
new CfStackInstruction(CfStackInstruction.Opcode.Pop),
label1,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 044b77f..843a0f3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ProgramOrClasspathDefinition;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.naming.NamingLens;
@@ -728,7 +729,7 @@
DexString name,
DexProto proto,
SyntheticKindSelector kindSelector,
- ProgramDefinition context,
+ ProgramOrClasspathDefinition context,
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> buildClassCallback,
Consumer<SyntheticMethodBuilder> buildMethodCallback) {
@@ -747,7 +748,7 @@
DexString name,
DexProto proto,
SyntheticKindSelector kindSelector,
- ProgramDefinition context,
+ ProgramOrClasspathDefinition context,
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> buildClassCallback,
Consumer<SyntheticMethodBuilder> buildMethodCallback,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 631115f..9aed303 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -43,6 +43,7 @@
public final SyntheticKind RETARGET_CLASS = generator.forFixedClass("RetargetClass");
public final SyntheticKind RETARGET_INTERFACE = generator.forFixedClass("RetargetInterface");
public final SyntheticKind WRAPPER = generator.forFixedClass("$Wrapper");
+ public final SyntheticKind VIVIFIED = generator.forFixedClass("");
public final SyntheticKind VIVIFIED_WRAPPER = generator.forFixedClass("$VivifiedWrapper");
public final SyntheticKind INIT_TYPE_ARGUMENT = generator.forFixedClass("-IA");
public final SyntheticKind HORIZONTAL_INIT_TYPE_ARGUMENT_1 =
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 9818d3e..037fafb 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.google.common.collect.ImmutableList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
@@ -55,6 +56,22 @@
return method.getFormalTypes().size() - other.getFormalTypes().size();
};
+ public static MethodReference classConstructor(Class<?> clazz) {
+ return classConstructor(Reference.classFromClass(clazz));
+ }
+
+ public static MethodReference classConstructor(ClassReference type) {
+ return Reference.classConstructor(type);
+ }
+
+ public static MethodReference instanceConstructor(Class<?> clazz) {
+ return instanceConstructor(Reference.classFromClass(clazz));
+ }
+
+ public static MethodReference instanceConstructor(ClassReference type) {
+ return Reference.method(type, "<init>", Collections.emptyList(), null);
+ }
+
public static int compare(MethodReference methodReference, ClassReference other) {
return ClassReferenceUtils.compare(other, methodReference) * -1;
}
diff --git a/src/test/java/android/util/Log.java b/src/test/java/android/util/Log.java
index 2ca06ef..67faa9e 100644
--- a/src/test/java/android/util/Log.java
+++ b/src/test/java/android/util/Log.java
@@ -8,6 +8,10 @@
public class Log {
+ public static int i(String tag, String message) {
+ throw new RuntimeException();
+ }
+
public static int v(String tag, String message) {
throw new RuntimeException();
}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index d5a943f..abce6c3 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -60,10 +60,6 @@
return !isNewerThanOrEqualTo(otherVersion);
}
- public boolean isOlderThanMinSupported() {
- return isOlderThan(KotlinCompilerVersion.MIN_SUPPORTED_VERSION);
- }
-
public boolean isFirst() {
return index == 0;
}
@@ -133,6 +129,11 @@
return this;
}
+ public Builder withOldCompiler(KotlinCompilerVersion oldVersion) {
+ oldCompilerFilter = oldCompilerFilter.and(v -> v.isEqualTo(oldVersion));
+ return this;
+ }
+
public Builder withAllTargetVersions() {
withTargetVersionFilter(t -> t != KotlinTargetVersion.NONE);
return this;
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 59d2bc6..cd34cd5 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -133,6 +133,10 @@
return withDexRuntimeFilter(vm -> true);
}
+ public TestParametersBuilder withDexRuntimesAndAllApiLevels() {
+ return withDexRuntimes().withAllApiLevels();
+ }
+
/** Add specific runtime if available. */
public TestParametersBuilder withDexRuntime(DexVm.Version runtime) {
return withDexRuntimeFilter(vm -> vm == runtime);
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
index ccb901d..962f964 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/VarHandleTest.java
@@ -94,6 +94,9 @@
.addProgramFiles(getProgramInputs())
.setMinApi(parameters.getApiLevel())
.mapUnsupportedFeaturesToWarnings()
+ // TODO(b/238175192): remove again when resolved
+ .addOptionsModification(
+ options -> options.enableUnrepresentableInDexInstructionRemoval = true)
.compileWithExpectedDiagnostics(
diagnostics -> {
if (hasInvokePolymorphicCompileSupport()) {
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
index 5435248..c706f50 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
@@ -4,37 +4,27 @@
package com.android.tools.r8.cfmethodgeneration;
-import static com.android.tools.r8.utils.PredicateUtils.not;
-
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.cf.CfCodePrinter;
import com.android.tools.r8.graph.ClassKind;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.JarApplicationReader;
import com.android.tools.r8.graph.JarClassFileReader;
-import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.Streams;
import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
-import java.util.function.Predicate;
public abstract class CfClassGenerator extends CodeGenerationBase {
@@ -54,27 +44,33 @@
private String generateClassDeclaration() throws IOException {
JavaStringBuilder builder = new JavaStringBuilder();
builder.append("public final class " + getGeneratedClassName() + " ").appendOpeningBrace();
- generateCreateClassMethod(builder);
- generateCreateFieldsMethod(builder, "createInstanceFields", not(FieldAccessFlags::isStatic));
- generateCreateFieldsMethod(builder, "createStaticFields", FieldAccessFlags::isStatic);
+ DexProgramClass clazz = readImplementationClass();
+ generateCreateClassMethod(builder, clazz);
+ generateCreateFieldsMethod(builder, "createInstanceFields", clazz.instanceFields());
+ generateCreateFieldsMethod(builder, "createStaticFields", clazz.staticFields());
CfCodePrinter codePrinter = new CfCodePrinter();
- Map<MethodReference, String> createCfCodeMethodNames = generateCreateCfCodeMethods(codePrinter);
+ Map<MethodReference, String> createCfCodeMethodNames =
+ generateCreateCfCodeMethods(clazz, codePrinter);
generateCreateMethodsMethod(
- builder,
- "createDirectMethods",
- MethodAccessFlags::belongsToDirectPool,
- createCfCodeMethodNames);
+ builder, "createDirectMethods", clazz.directMethods(), createCfCodeMethodNames);
generateCreateMethodsMethod(
- builder,
- "createVirtualMethods",
- MethodAccessFlags::belongsToVirtualPool,
- createCfCodeMethodNames);
+ builder, "createVirtualMethods", clazz.virtualMethods(), createCfCodeMethodNames);
codePrinter.getMethods().forEach(builder::appendLine);
builder.appendClosingBrace();
return builder.toString();
}
- private void generateCreateClassMethod(JavaStringBuilder builder) {
+ private DexProgramClass readImplementationClass() throws IOException {
+ InternalOptions options = new InternalOptions(factory, new Reporter());
+ options.testing.readInputStackMaps = true;
+ Box<DexProgramClass> result = new Box<>();
+ JarClassFileReader<DexProgramClass> reader =
+ new JarClassFileReader<>(new JarApplicationReader(options), result::set, ClassKind.PROGRAM);
+ reader.read(Origin.unknown(), ToolHelper.getClassAsBytes(getImplementation()));
+ return result.get();
+ }
+
+ private void generateCreateClassMethod(JavaStringBuilder builder, DexProgramClass clazz) {
builder
.startLine()
.append("public static ")
@@ -93,7 +89,7 @@
builder
.startLine()
.append("dexItemFactory.createType(\"")
- .append(getGeneratedClassDescriptor())
+ .append(clazz.getType().toDescriptorString())
.appendLine("\"),");
builder.startLine().append(imports.getProgramResourceKind()).appendLine(".CF,");
@@ -104,17 +100,21 @@
.startLine()
.append(imports.getClassAccessFlags())
.append(".fromCfAccessFlags(")
- .append(getImplementation().getModifiers())
+ .append(clazz.getAccessFlags().getAsCfAccessFlags())
.appendLine("),");
- builder.startLine().appendLine("null,");
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(clazz.getSuperType().toDescriptorString())
+ .appendLine("\"),");
builder.startLine().append(imports.getDexTypeList()).appendLine(".empty(),");
builder
.startLine()
.append("dexItemFactory.createString(\"")
- .append(getGeneratedClassName())
+ .append(clazz.getSourceFile().toString())
.appendLine("\"),");
builder.startLine().append(imports.getNestHostClassAttribute()).appendLine(".none(),");
@@ -151,7 +151,7 @@
}
private void generateCreateFieldsMethod(
- JavaStringBuilder builder, String methodName, Predicate<FieldAccessFlags> predicate) {
+ JavaStringBuilder builder, String methodName, List<DexEncodedField> fields) {
builder
.startLine()
.append("private static ")
@@ -170,73 +170,61 @@
.append("[] ")
.appendOpeningArrayBrace();
- Iterator<Field> fieldIterator =
- Arrays.stream(getImplementation().getDeclaredFields())
- .filter(
- field -> predicate.test(FieldAccessFlags.fromCfAccessFlags(field.getModifiers())))
- .sorted(
- (x, y) ->
- FieldReferenceUtils.compare(
- Reference.fieldFromField(x), Reference.fieldFromField(y)))
- .iterator();
+ Iterator<DexEncodedField> fieldIterator = fields.iterator();
while (fieldIterator.hasNext()) {
- Field field = fieldIterator.next();
- FieldAccessFlags flags = FieldAccessFlags.fromCfAccessFlags(field.getModifiers());
- if (predicate.test(flags)) {
- builder
- .startLine()
- .append(imports.getDexEncodedField())
- .appendLine(".syntheticBuilder()")
- .indent(4);
+ DexEncodedField field = fieldIterator.next();
+ assert !field.isStatic() || !field.hasExplicitStaticValue();
- builder.startLine().append(".setField").appendOpeningMultiLineParenthesis();
+ builder
+ .startLine()
+ .append(imports.getDexEncodedField())
+ .appendLine(".syntheticBuilder()")
+ .indent(4);
- builder
- .startLine()
- .append("dexItemFactory.createField")
- .appendOpeningMultiLineParenthesis();
+ builder.startLine().append(".setField").appendOpeningMultiLineParenthesis();
- builder
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(field.getDeclaringClass()))
- .appendLine("\"),");
+ builder.startLine().append("dexItemFactory.createField").appendOpeningMultiLineParenthesis();
- builder
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(field.getType()))
- .appendLine("\"),");
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(field.getHolderType().toDescriptorString())
+ .appendLine("\"),");
- builder
- .startLine()
- .append("dexItemFactory.createString(\"")
- .append(field.getName())
- .append("\")")
- .appendClosingMultiLineParenthesis()
- .appendClosingMultiLineParenthesis()
- .appendLine();
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(field.getType().toDescriptorString())
+ .appendLine("\"),");
- builder
- .startLine()
- .append(".setAccessFlags(")
- .append(imports.getFieldAccessFlags())
- .append(".fromCfAccessFlags(")
- .append(field.getModifiers())
- .appendLine("))");
+ builder
+ .startLine()
+ .append("dexItemFactory.createString(\"")
+ .append(field.getName().toString())
+ .append("\")")
+ .appendClosingMultiLineParenthesis()
+ .appendClosingMultiLineParenthesis()
+ .appendLine();
- builder
- .startLine()
- .append(".setApiLevel(")
- .append(imports.getComputedApiLevel())
- .appendLine(".unknown())");
+ builder
+ .startLine()
+ .append(".setAccessFlags(")
+ .append(imports.getFieldAccessFlags())
+ .append(".fromCfAccessFlags(")
+ .append(field.getAccessFlags().getAsCfAccessFlags())
+ .appendLine("))");
- builder.startLine().append(".build()").indent(-4);
- if (fieldIterator.hasNext()) {
- builder.appendLine(',');
- } else {
- builder.appendLine();
- }
+ builder
+ .startLine()
+ .append(".setApiLevel(")
+ .append(imports.getComputedApiLevel())
+ .appendLine(".unknown())");
+
+ builder.startLine().append(".build()").indent(-4);
+ if (fieldIterator.hasNext()) {
+ builder.appendLine(',');
+ } else {
+ builder.appendLine();
}
}
@@ -247,7 +235,7 @@
private void generateCreateMethodsMethod(
JavaStringBuilder builder,
String methodName,
- Predicate<MethodAccessFlags> predicate,
+ Iterable<DexEncodedMethod> methods,
Map<MethodReference, String> createCfCodeMethodNames) {
builder
.startLine()
@@ -267,23 +255,9 @@
.append("[] ")
.appendOpeningArrayBrace();
- getImplementation().getDeclaredConstructors();
-
- Iterator<Executable> executableIterator =
- Streams.concat(
- Arrays.stream(getImplementation().getDeclaredConstructors()),
- Arrays.stream(getImplementation().getDeclaredMethods()))
- .filter(
- executable ->
- predicate.test(
- MethodAccessFlags.fromCfAccessFlags(executable.getModifiers(), false)))
- .sorted(
- (x, y) ->
- MethodReferenceUtils.compare(
- Reference.methodFromMethod(x), Reference.methodFromMethod(y)))
- .iterator();
- while (executableIterator.hasNext()) {
- Executable executable = executableIterator.next();
+ Iterator<DexEncodedMethod> methodIterator = methods.iterator();
+ while (methodIterator.hasNext()) {
+ DexEncodedMethod method = methodIterator.next();
builder
.startLine()
.append(imports.getDexEncodedMethod())
@@ -295,8 +269,9 @@
.append(".setAccessFlags(")
.append(imports.getMethodAccessFlags())
.append(".fromCfAccessFlags(")
- .append(executable.getModifiers())
- .append(", false")
+ .append(method.getAccessFlags().getAsCfAccessFlags())
+ .append(", ")
+ .append(method.isInitializer())
.appendLine("))");
builder
@@ -319,74 +294,42 @@
builder.startLine().append(".setMethod").appendOpeningMultiLineParenthesis();
- if (executable instanceof Constructor<?>) {
- Constructor<?> constructor = (Constructor<?>) executable;
- builder
- .startLine()
- .append("dexItemFactory.createInstanceInitializer")
- .appendOpeningMultiLineParenthesis();
+ builder.startLine().append("dexItemFactory.createMethod").appendOpeningMultiLineParenthesis();
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(method.getHolderType().toDescriptorString())
+ .appendLine("\"),");
+
+ builder.startLine().append("dexItemFactory.createProto").appendOpeningMultiLineParenthesis();
+
+ builder
+ .startLine()
+ .append("dexItemFactory.createType(\"")
+ .append(method.getReturnType().toDescriptorString())
+ .append("\")");
+
+ for (DexType parameter : method.getParameters()) {
builder
+ .appendLine(",")
.startLine()
.append("dexItemFactory.createType(\"")
- .append(descriptor(constructor.getDeclaringClass()))
- .append("\")");
-
- for (Class<?> parameter : constructor.getParameterTypes()) {
- builder
- .appendLine(",")
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(parameter))
- .append("\")");
- }
- } else {
- assert executable instanceof Method;
- Method method = (Method) executable;
-
- builder
- .startLine()
- .append("dexItemFactory.createMethod")
- .appendOpeningMultiLineParenthesis();
-
- builder
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(method.getDeclaringClass()))
- .appendLine("\"),");
-
- builder
- .startLine()
- .append("dexItemFactory.createProto")
- .appendOpeningMultiLineParenthesis();
-
- builder
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(method.getReturnType()))
- .append("\")");
-
- for (Class<?> parameter : method.getParameterTypes()) {
- builder
- .appendLine(",")
- .startLine()
- .append("dexItemFactory.createType(\"")
- .append(descriptor(parameter))
- .append("\")");
- }
- builder.appendClosingMultiLineParenthesis().appendLine(',');
-
- builder
- .startLine()
- .append("dexItemFactory.createString(\"")
- .append(method.getName())
+ .append(parameter.toDescriptorString())
.append("\")");
}
+ builder.appendClosingMultiLineParenthesis().appendLine(',');
+
+ builder
+ .startLine()
+ .append("dexItemFactory.createString(\"")
+ .append(method.getName().toString())
+ .append("\")");
builder.appendClosingMultiLineParenthesis().appendClosingMultiLineParenthesis().appendLine();
String createCfCodeMethodName =
- createCfCodeMethodNames.get(Reference.methodFromMethod(executable));
+ createCfCodeMethodNames.get(method.getReference().asMethodReference());
if (createCfCodeMethodName != null) {
builder
.startLine()
@@ -396,7 +339,7 @@
}
builder.startLine().append(".build()").indent(-4);
- if (executableIterator.hasNext()) {
+ if (methodIterator.hasNext()) {
builder.appendLine(',');
} else {
builder.appendLine();
@@ -407,29 +350,19 @@
builder.appendClosingBrace();
}
- private Map<MethodReference, String> generateCreateCfCodeMethods(CfCodePrinter codePrinter)
- throws IOException {
+ private Map<MethodReference, String> generateCreateCfCodeMethods(
+ DexProgramClass clazz, CfCodePrinter codePrinter) {
Map<MethodReference, String> createCfCodeMethodNames = new HashMap<>();
- InternalOptions options = new InternalOptions(factory, new Reporter());
- options.testing.readInputStackMaps = true;
- JarClassFileReader<DexProgramClass> reader =
- new JarClassFileReader<>(
- new JarApplicationReader(options),
- clazz -> {
- int index = 0;
- for (DexEncodedMethod method : clazz.allMethodsSorted()) {
- if (!method.hasCode()) {
- continue;
- }
- String generatedMethodName = getCreateCfCodeMethodName(method, index);
- createCfCodeMethodNames.put(
- method.getReference().asMethodReference(), generatedMethodName);
- codePrinter.visitMethod(generatedMethodName, method.getCode().asCfCode());
- index++;
- }
- },
- ClassKind.PROGRAM);
- reader.read(Origin.unknown(), ToolHelper.getClassAsBytes(getImplementation()));
+ int index = 0;
+ for (DexEncodedMethod method : clazz.allMethodsSorted()) {
+ if (!method.hasCode()) {
+ continue;
+ }
+ String generatedMethodName = getCreateCfCodeMethodName(method, index);
+ createCfCodeMethodNames.put(method.getReference().asMethodReference(), generatedMethodName);
+ codePrinter.visitMethod(generatedMethodName, method.getCode().asCfCode());
+ index++;
+ }
codePrinter.getImports().forEach(imports::addImport);
return createCfCodeMethodNames;
}
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java
index 7de114a..8d8d44a 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/JavaStringBuilder.java
@@ -9,6 +9,11 @@
private final StringBuilder builder = new StringBuilder();
private int indentation = 0;
+ public JavaStringBuilder append(boolean b) {
+ builder.append(b);
+ return this;
+ }
+
public JavaStringBuilder append(char c) {
builder.append(c);
return this;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index e77e3d9..2a325ad 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
@@ -54,8 +53,8 @@
startupClasses.forEach(
startupClass ->
builder.addStartupClass(
- StartupClass.<DexType>builder()
- .setReference(
+ StartupClass.dexBuilder()
+ .setClassReference(
toDexType(startupClass, dexItemFactory))
.build())))
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index d90495c..8fc32b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.desugaredlibrary;
+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 com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static org.junit.Assert.assertEquals;
@@ -14,11 +16,17 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
@@ -31,6 +39,7 @@
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -44,6 +53,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,26 +63,25 @@
@RunWith(Parameterized.class)
public class ExtractWrapperTypesTest extends DesugaredLibraryTestBase {
- // Filter on types that do not need to be considered for wrapping.
- private static boolean doesNotNeedWrapper(String type, Set<String> customConversions) {
- return excludePackage(type)
- || NOT_NEEDED_NOT_IN_DOCS.contains(type)
- || FINAL_CLASSES.contains(type)
- || customConversions.contains(type);
- }
-
- private static boolean excludePackage(String type) {
- return type.startsWith("java.lang.")
- || type.startsWith("java.nio.")
- || type.startsWith("java.security.")
- || type.startsWith("java.net.")
- || type.startsWith("java.awt.")
- || type.startsWith("java.util.concurrent.");
- }
-
// Types not picked up by the android.jar scan but for which wrappers are needed.
private static final Set<String> ADDITIONAL_WRAPPERS = ImmutableSet.of();
+ private static final Set<String> GENERIC_NOT_NEEDED =
+ ImmutableSet.of("java.util.String", "java.util.Locale$LanguageRange");
+
+ // We need wrappers for only a subset of java.nio.channels. The whole package is marked as
+ // needing wrappers and this is the exclusion set.
+ private static final Set<String> NOT_NEEDED =
+ ImmutableSet.of(
+ "java.nio.channels.AsynchronousByteChannel",
+ "java.nio.channels.AsynchronousChannelGroup",
+ "java.nio.channels.AsynchronousServerSocketChannel",
+ "java.nio.channels.AsynchronousSocketChannel",
+ "java.nio.channels.MembershipKey",
+ "java.nio.channels.MulticastChannel",
+ "java.nio.channels.NetworkChannel",
+ "java.nio.channels.spi.AsynchronousChannelProvider");
+
// Types not in API docs, referenced in android.jar and must be wrapped.
private static final Set<String> NEEDED_BUT_NOT_IN_DOCS = ImmutableSet.of();
@@ -90,18 +99,43 @@
"java.util.Locale$FilteringMode",
"java.util.SplittableRandom");
- // List of referenced final classes (cannot be wrapper converted) with no custom conversions.
- private static final Set<String> FINAL_CLASSES =
+ // TODO(b/238179854): Investigate how to fix these.
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION =
ImmutableSet.of(
- // TODO(b/159304624): Does this need custom conversion?
- "java.time.Period");
+ "java.util.stream.Stream java.util.stream.Stream.flatMap(java.util.function.Function)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.DoubleStream.flatMap(java.util.function.DoubleFunction)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.Stream.flatMapToDouble(java.util.function.Function)",
+ "java.util.stream.IntStream"
+ + " java.util.stream.Stream.flatMapToInt(java.util.function.Function)",
+ "java.util.stream.IntStream"
+ + " java.util.stream.IntStream.flatMap(java.util.function.IntFunction)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.Stream.flatMapToLong(java.util.function.Function)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.LongStream.flatMap(java.util.function.LongFunction)");
+
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_8 =
+ ImmutableSet.of("java.util.Set java.util.stream.Collector.characteristics()");
+
+ // TODO(b/238179854): Investigate how to fix these.
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_PATH =
+ ImmutableSet.of(
+ "java.lang.Iterable java.nio.file.FileSystem.getFileStores()",
+ "java.lang.Iterable java.nio.file.FileSystem.getRootDirectories()",
+ "java.util.Iterator java.nio.file.Path.iterator()",
+ "java.nio.file.DirectoryStream"
+ + " java.nio.file.spi.FileSystemProvider.newDirectoryStream(java.nio.file.Path,"
+ + " java.nio.file.DirectoryStream$Filter)");
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@Parameters(name = "{0}, spec: {1}")
public static List<Object[]> data() {
// TODO(b/236356665): Support JDK11 desugared lib.
- return buildParameters(getTestParameters().withNoneRuntime().build(), ImmutableList.of(JDK8));
+ return buildParameters(
+ getTestParameters().withNoneRuntime().build(), ImmutableList.of(JDK8, JDK11, JDK11_PATH));
}
public ExtractWrapperTypesTest(
@@ -112,13 +146,23 @@
// TODO: parameterize to check both api<=23 as well as 23<api<26 for which the spec differs.
private final AndroidApiLevel minApi = AndroidApiLevel.B;
- private final AndroidApiLevel targetApi = AndroidApiLevel.Q;
+ private final AndroidApiLevel targetApi = AndroidApiLevel.S;
+
+ private Set<String> getMissingGenericTypeConversions() {
+ HashSet<String> missing = new HashSet<>(MISSING_GENERIC_TYPE_CONVERSION);
+ if (libraryDesugaringSpecification == JDK8) {
+ missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_8);
+ }
+ if (libraryDesugaringSpecification == JDK11_PATH) {
+ missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_PATH);
+ }
+ return missing;
+ }
@Test
public void checkConsistency() {
List<Set<String>> sets =
- ImmutableList.of(
- ADDITIONAL_WRAPPERS, NEEDED_BUT_NOT_IN_DOCS, NOT_NEEDED_NOT_IN_DOCS, FINAL_CLASSES);
+ ImmutableList.of(ADDITIONAL_WRAPPERS, NEEDED_BUT_NOT_IN_DOCS, NOT_NEEDED_NOT_IN_DOCS);
for (Set<String> set1 : sets) {
for (Set<String> set2 : sets) {
if (set1 != set2) {
@@ -133,6 +177,26 @@
}
}
+ // Filter on types that do not need to be considered for wrapping.
+ private boolean doesNotNeedWrapper(
+ String type, Set<String> customConversions, Set<String> maintainType) {
+ return excludePackage(type)
+ || NOT_NEEDED_NOT_IN_DOCS.contains(type)
+ || NOT_NEEDED.contains(type)
+ || customConversions.contains(type)
+ || maintainType.contains(type);
+ }
+
+ private boolean excludePackage(String type) {
+ return type.startsWith("java.lang.")
+ || type.startsWith("java.security.")
+ || type.startsWith("java.net.")
+ || type.startsWith("java.awt.")
+ || type.startsWith("java.util.concurrent.")
+ || (!libraryDesugaringSpecification.hasNioFileDesugaring(AndroidApiLevel.B)
+ && type.startsWith("java.nio."));
+ }
+
@Test
public void test() throws Exception {
CodeInspector desugaredApiJar = getDesugaredApiJar();
@@ -159,17 +223,45 @@
specification.getCustomConversions().keySet().stream()
.map(DexType::toString)
.collect(Collectors.toSet());
+ Set<String> maintainTypeInSet =
+ specification.getMaintainType().stream().map(DexType::toString).collect(Collectors.toSet());
assertEquals(
Collections.emptySet(), Sets.intersection(wrappersInSpec, customConversionsInSpec));
- assertEquals(Collections.emptySet(), Sets.intersection(FINAL_CLASSES, customConversionsInSpec));
CodeInspector nonDesugaredJar = new CodeInspector(ToolHelper.getAndroidJar(targetApi));
+ Set<DexEncodedMethod> genericDependencies = new HashSet<>();
Map<ClassReference, Set<MethodReference>> directWrappers =
getDirectlyReferencedWrapperTypes(
- desugaredApiJar, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+ desugaredApiJar,
+ preDesugarTypes,
+ nonDesugaredJar,
+ customConversionsInSpec,
+ maintainTypeInSet,
+ genericDependencies);
Map<ClassReference, Set<ClassReference>> indirectWrappers =
getIndirectlyReferencedWrapperTypes(
- directWrappers, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+ directWrappers,
+ preDesugarTypes,
+ nonDesugaredJar,
+ customConversionsInSpec,
+ maintainTypeInSet,
+ specification.getWrappers(),
+ genericDependencies);
+ {
+ Set<String> missingGenericDependency = new HashSet<>();
+ for (DexEncodedMethod genericDependency : genericDependencies) {
+ if (!specification
+ .getApiGenericConversion()
+ .containsKey(genericDependency.getReference())) {
+ missingGenericDependency.add(genericDependency.getReference().toString());
+ }
+ }
+ // TODO(b/236356665): There should be no missing conversion.
+ assertEquals(
+ "Missing generic type conversion:\n" + String.join("\n", missingGenericDependency),
+ getMissingGenericTypeConversions(),
+ missingGenericDependency);
+ }
{
Set<String> missingWrappers = getMissingWrappers(directWrappers, wrappersInSpec);
@@ -178,11 +270,17 @@
missingWrappers.isEmpty());
}
+ // java.util.stream.Collector$Characteristics is required for api generic type conversion
+ // on JDK8, but that is not supported on legacy specification used for JDK8 and on old
+ // R8 compiler versions.
+ int expectedMissingWrappers = libraryDesugaringSpecification == JDK8 ? 1 : 0;
+
{
Set<String> missingWrappers = getMissingWrappers(indirectWrappers, wrappersInSpec);
- assertTrue(
+ assertEquals(
"Missing indirect wrappers:\n" + String.join("\n", missingWrappers),
- missingWrappers.isEmpty());
+ expectedMissingWrappers,
+ missingWrappers.size());
}
Set<String> additionalWrappers = new TreeSet<>();
@@ -200,7 +298,7 @@
assertEquals(
directWrappers.size() + indirectWrappers.size() + ADDITIONAL_WRAPPERS.size(),
- wrappersInSpec.size());
+ wrappersInSpec.size() + expectedMissingWrappers);
}
private static <T> Set<String> getMissingWrappers(
@@ -219,7 +317,9 @@
CodeInspector desugaredApiJar,
Set<ClassReference> preDesugarTypes,
CodeInspector nonDesugaredJar,
- Set<String> customConversions) {
+ Set<String> customConversions,
+ Set<String> maintainType,
+ Set<DexEncodedMethod> genericDependencies) {
Map<ClassReference, Set<MethodReference>> directWrappers = new HashMap<>();
nonDesugaredJar.forAllClasses(
clazz -> {
@@ -228,7 +328,12 @@
if (!method.isPublic() && !method.isProtected()) {
return;
}
- if (desugaredApiJar.method(method.asMethodReference()).isPresent()) {
+ // We check the holder type to avoid dealing with methods on desugared types which
+ // are present in Android.jar and not in the desugared library, specifically on
+ // JDK 8 desugared library.
+ if (desugaredApiJar
+ .clazz(method.getMethod().getHolderType().asClassReference())
+ .isPresent()) {
return;
}
Consumer<ClassReference> adder =
@@ -236,41 +341,83 @@
directWrappers
.computeIfAbsent(t, k -> new HashSet<>())
.add(method.asMethodReference());
- MethodSignature signature = method.getFinalSignature().asMethodSignature();
- addType(adder, signature.type, preDesugarTypes, customConversions);
- for (String parameter : signature.parameters) {
- addType(adder, parameter, preDesugarTypes, customConversions);
- }
+ forEachType(
+ method,
+ t -> addType(adder, t, preDesugarTypes, customConversions, maintainType),
+ genericDependencies);
});
});
return directWrappers;
}
+ private void forEachType(
+ FoundMethodSubject subject,
+ Function<String, Boolean> process,
+ Set<DexEncodedMethod> generics) {
+ MethodSignature signature = subject.getFinalSignature().asMethodSignature();
+ process.apply(signature.type);
+ for (String parameter : signature.parameters) {
+ process.apply(parameter);
+ }
+ MethodTypeSignature genericSignature = subject.getMethod().getGenericSignature();
+ if (genericSignature != null) {
+ TypeSignature[] typeSignatures = new TypeSignature[signature.parameters.length + 1];
+ for (int i = 0; i < signature.parameters.length; i++) {
+ typeSignatures[i] = genericSignature.getParameterTypeSignature(i);
+ }
+ typeSignatures[signature.parameters.length] = genericSignature.returnType().typeSignature();
+ for (TypeSignature typeSignature : typeSignatures) {
+ if ((typeSignature instanceof ClassTypeSignature)) {
+ for (FieldTypeSignature typeArgument :
+ ((ClassTypeSignature) typeSignature).typeArguments()) {
+ if (typeArgument instanceof ClassTypeSignature) {
+ String type = descriptorToJavaType(typeArgument.toString()).split("<")[0];
+ if (!GENERIC_NOT_NEEDED.contains(type)) {
+ boolean added = process.apply(type);
+ if (added) {
+ generics.add(subject.getMethod());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
private Map<ClassReference, Set<ClassReference>> getIndirectlyReferencedWrapperTypes(
Map<ClassReference, Set<MethodReference>> directWrappers,
Set<ClassReference> existing,
CodeInspector latest,
- Set<String> customConversions) {
+ Set<String> customConversions,
+ Set<String> maintainType,
+ Map<DexType, WrapperDescriptor> wrapperDescriptorMap,
+ Set<DexEncodedMethod> genericDependencies) {
Map<ClassReference, Set<ClassReference>> indirectWrappers = new HashMap<>();
WorkList<ClassReference> worklist = WorkList.newEqualityWorkList(directWrappers.keySet());
while (worklist.hasNext()) {
ClassReference reference = worklist.next();
ClassSubject clazz = latest.clazz(reference);
+ Consumer<ClassReference> adder =
+ t -> {
+ if (worklist.addIfNotSeen(t)) {
+ indirectWrappers.computeIfAbsent(t, k -> new HashSet<>()).add(reference);
+ }
+ };
clazz.forAllVirtualMethods(
method -> {
assertTrue(method.toString(), method.isPublic() || method.isProtected());
- MethodSignature signature = method.getFinalSignature().asMethodSignature();
- Consumer<ClassReference> adder =
- t -> {
- if (worklist.addIfNotSeen(t)) {
- indirectWrappers.computeIfAbsent(t, k -> new HashSet<>()).add(reference);
- }
- };
- addType(adder, signature.type, existing, customConversions);
- for (String parameter : signature.parameters) {
- addType(adder, parameter, existing, customConversions);
- }
+ forEachType(
+ method,
+ t -> addType(adder, t, existing, customConversions, maintainType),
+ genericDependencies);
});
+ WrapperDescriptor descriptor = wrapperDescriptorMap.get(clazz.getDexProgramClass().getType());
+ if (descriptor != null) {
+ for (DexType subwrapper : descriptor.getSubwrappers()) {
+ addType(adder, subwrapper.getTypeName(), existing, customConversions, maintainType);
+ }
+ }
}
return indirectWrappers;
}
@@ -301,13 +448,14 @@
.resolve("desugared_apis_" + targetApi.getLevel() + "_" + minApi.getLevel() + ".jar"));
}
- private void addType(
+ private boolean addType(
Consumer<ClassReference> additions,
String type,
Set<ClassReference> preDesugarTypes,
- Set<String> customConversions) {
+ Set<String> customConversions,
+ Set<String> maintainType) {
if (type.equals("void")) {
- return;
+ return false;
}
TypeReference typeReference = Reference.typeFromTypeName(type);
if (typeReference.isArray()) {
@@ -317,10 +465,14 @@
ClassReference clazz = typeReference.asClass();
String clazzType = descriptorToJavaType(clazz.getDescriptor());
if (clazzType.startsWith("java.")
- && !doesNotNeedWrapper(clazzType, customConversions)
- && !preDesugarTypes.contains(clazz)) {
+ && !doesNotNeedWrapper(clazzType, customConversions, maintainType)
+ // FileChannel is there since B but it needs wrapping due to recently added interfaces.
+ && (!preDesugarTypes.contains(clazz)
+ || clazzType.equals("java.nio.channels.FileChannel"))) {
additions.accept(clazz);
+ return true;
}
}
+ return false;
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/LocalTimePeriodConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/LocalTimePeriodConversionTest.java
new file mode 100644
index 0000000..2dbc3d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/LocalTimePeriodConversionTest.java
@@ -0,0 +1,86 @@
+// 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.conversiontests;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+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.CustomLibrarySpecification;
+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.google.common.collect.ImmutableList;
+import java.time.LocalTime;
+import java.time.Period;
+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 LocalTimePeriodConversionTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT = StringUtils.lines("00:00", "P0D");
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED),
+ ImmutableList.of(JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public LocalTimePeriodConversionTest(
+ 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)
+ .addProgramClasses(Executor.class)
+ .setCustomLibrarySpecification(
+ new CustomLibrarySpecification(CustomLibClass.class, MIN_SUPPORTED))
+ .addKeepMainRule(Executor.class)
+ .compile()
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ System.out.println(CustomLibClass.get(LocalTime.MIDNIGHT));
+ System.out.println(CustomLibClass.get(Period.ZERO));
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class CustomLibClass {
+
+ public static LocalTime get(LocalTime localTime) {
+ return localTime;
+ }
+
+ public static Period get(Period period) {
+ return period;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
index 278ec26..a61445b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
@@ -14,9 +14,12 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
import java.util.List;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
+import java.util.stream.BaseStream;
+import java.util.stream.IntStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -30,7 +33,7 @@
private final CompilationSpecification compilationSpecification;
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
- private static final String EXPECTED_RESULT = StringUtils.lines("true", "true");
+ private static final String EXPECTED_RESULT = StringUtils.lines("true", "true", "true", "true");
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
@@ -64,6 +67,22 @@
@SuppressWarnings("all")
public static void main(String[] args) {
+ consumerTest();
+ streamTest();
+ }
+
+ private static void streamTest() {
+ // Type wrapper.
+ IntStream intStream = Arrays.stream(new int[] {1});
+ BaseStream<?, ?> unwrapped = CustomLibClass.identity(intStream);
+ System.out.println(unwrapped == intStream);
+
+ // Vivified wrapper.
+ IntStream consumer = CustomLibClass.getStream();
+ System.out.println(CustomLibClass.testStream(consumer));
+ }
+
+ private static void consumerTest() {
// Type wrapper.
IntConsumer intConsumer = i -> {};
IntConsumer unwrappedIntConsumer = CustomLibClass.identity(intConsumer);
@@ -95,5 +114,21 @@
public static boolean testConsumer(DoubleConsumer doubleConsumer) {
return doubleConsumer == consumer;
}
+
+ private static IntStream intStream = Arrays.stream(new int[] {0});
+
+ @SuppressWarnings("WeakerAccess")
+ public static BaseStream<Integer, IntStream> identity(IntStream arg) {
+ return arg;
+ }
+
+ public static IntStream getStream() {
+ return intStream;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static boolean testStream(BaseStream<?, ?> stream) {
+ return stream == intStream;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NameLimitsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NameLimitsTest.java
new file mode 100644
index 0000000..44e6911
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NameLimitsTest.java
@@ -0,0 +1,92 @@
+// 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.jdktests;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.EXTENSION_PATH;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+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.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+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 Jdk11NameLimitsTest extends DesugaredLibraryTestBase {
+
+ private static final Path TEST_PATH =
+ Paths.get(ToolHelper.JDK_11_TESTS_DIR).resolve("java/nio/file/Files/NameLimits.java");
+ private static Path[] COMPILED_TEST_PATH;
+
+ private final TestParameters parameters;
+ private final CompilationSpecification compilationSpecification;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(LibraryDesugaringSpecification.JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public Jdk11NameLimitsTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.compilationSpecification = compilationSpecification;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ }
+
+ @BeforeClass
+ public static void compileJdk11NioTests() throws Exception {
+ List<String> options =
+ Arrays.asList(
+ "--add-reads",
+ "java.base=ALL-UNNAMED",
+ "--patch-module",
+ "java.base=" + EXTENSION_PATH);
+ Path tmpDirectory = getStaticTemp().newFolder("cmp").toPath();
+ javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
+ .addOptions(options)
+ .addSourceFiles(TEST_PATH)
+ .setOutputPath(tmpDirectory)
+ .compile();
+ COMPILED_TEST_PATH = getAllFilesWithSuffixInDirectory(tmpDirectory, CLASS_EXTENSION);
+ assert COMPILED_TEST_PATH.length > 0;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ Assume.assumeFalse(
+ "The behavior is invalid on 13 even without desugared library.",
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().equals(Version.V13_0_0));
+ Assume.assumeFalse(
+ "Dead lock issue.",
+ parameters.isDexRuntime()
+ && libraryDesugaringSpecification.hasNioFileDesugaring(parameters)
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V10_0_0));
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(COMPILED_TEST_PATH)
+ .addKeepMainRule("NameLimits")
+ .run(parameters.getRuntime(), "NameLimits")
+ .assertSuccess();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
index 8168e33..8274628 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
@@ -74,7 +74,7 @@
// TODO(134732760): Support Dalvik VMs, currently fails because libjavacrypto is required
// and present only in ART runtimes.
getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withDexRuntimesStartingFromIncluding(Version.V10_0_0)
.withAllApiLevels()
.build(),
specs,
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 b3cc9ab..70fbd07 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
@@ -267,7 +267,11 @@
}
public boolean hasNioFileDesugaring(TestParameters parameters) {
- return parameters.getApiLevel().getLevel() < descriptor.getNioFileDesugaring();
+ return hasNioFileDesugaring(parameters.getApiLevel());
+ }
+
+ public boolean hasNioFileDesugaring(AndroidApiLevel apiLevel) {
+ return apiLevel.getLevel() < descriptor.getNioFileDesugaring();
}
public boolean hasNioChannelDesugaring(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
index d4ac494..4646691 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
@@ -53,7 +53,6 @@
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- // TODO(b/129200243): Should only have two appends.
assertEquals(3, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
index c28729a..cbe8fa7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +53,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): Should be String.concat.
- assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(2, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
index d2c95ab..8005ed5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
@@ -7,12 +7,14 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +54,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): This should just be arg.toString()
- assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
index 9b9280f..0f7d286 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +53,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): Should be String.concat.
- assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
});
}
@@ -63,7 +64,7 @@
public static void main(String[] args) {
String arg = System.currentTimeMillis() > 0 ? "o" : null;
if (arg != null) {
- // The optimization should join "ok" into init and then append with arg.
+ // The optimization should join "ok" into init and then concat with arg.
System.out.println(new StringBuilder("o").append("k").append(arg).toString());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatConstantTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatConstantTest.java
new file mode 100644
index 0000000..608c784
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatConstantTest.java
@@ -0,0 +1,68 @@
+// 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.ir.optimize.string;
+
+import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
+import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 StringConcatImplicitToStringConcatConstantTest extends TestBase {
+
+ private static final String EXPECTED = "oko";
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(
+ inspect -> {
+ MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
+ assertThat(methodSubject, isPresent());
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ });
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ String arg1 = System.currentTimeMillis() > 0 ? "o" : "";
+ System.out.println(
+ new StringBuilder(new StringBuilder(arg1).append("k").append("o")).toString());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatMultipleTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatMultipleTest.java
new file mode 100644
index 0000000..f05e84f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatMultipleTest.java
@@ -0,0 +1,75 @@
+// 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.ir.optimize.string;
+
+import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
+import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 StringConcatImplicitToStringConcatMultipleTest extends TestBase {
+
+ private static final String EXPECTED = "okok";
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(
+ inspect -> {
+ MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
+ assertThat(methodSubject, isPresent());
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ });
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ String arg1 = System.currentTimeMillis() > 0 ? "o" : "";
+ String arg2 = System.currentTimeMillis() > 0 ? "k" : "";
+ System.out.println(
+ new StringBuilder()
+ .append(
+ new StringBuilder()
+ .append(new StringBuilder().append(arg1).append(arg2))
+ .append(arg1))
+ .append(arg2)
+ .toString());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatTest.java
index a2eb814..4c9ec93 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringConcatTest.java
@@ -52,20 +52,17 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): This should be String.concat() as argument.
- assertEquals(2, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
public static class Main {
public static void main(String[] args) {
- String arg1 = System.currentTimeMillis() > 0 ? "o" : null;
- String arg2 = System.currentTimeMillis() > 0 ? "k" : null;
- if (arg1 != null && arg2 != null) {
- System.out.println(new StringBuilder(new StringBuilder(arg1).append(arg2)).toString());
- }
+ String arg1 = System.currentTimeMillis() > 0 ? "o" : "";
+ String arg2 = System.currentTimeMillis() > 0 ? "k" : "";
+ System.out.println(new StringBuilder(new StringBuilder(arg1).append(arg2)).toString());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringSingleTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringSingleTest.java
index a3eb034..f42b6a5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringSingleTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatImplicitToStringSingleTest.java
@@ -52,8 +52,7 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): Use the arg string directly as input.
- assertEquals(2, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
@@ -61,10 +60,8 @@
public static class Main {
public static void main(String[] args) {
- String arg = System.currentTimeMillis() > 0 ? "o" : null;
- if (arg != null) {
- System.out.println(new StringBuilder(new StringBuilder(arg)).toString());
- }
+ String arg = System.currentTimeMillis() > 0 ? "o" : "";
+ System.out.println(new StringBuilder(new StringBuilder(arg)).toString());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
index 4dce8fd..aca301c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +53,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): Should be concat.
- assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
index 690a604..4bb550e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
@@ -7,12 +7,14 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,9 +54,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- // TODO(b/129200243): This should just be the string as is.
- assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
index 73e61bc..33320dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
@@ -7,12 +7,14 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +56,7 @@
assertThat(methodSubject, isPresent());
assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
+ assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
});
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index 5f6030f..f2792a4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -42,7 +43,11 @@
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build(),
+ getKotlinTestParameters()
+ .withOldCompilersIfSet()
+ .withOldCompiler(KotlinCompilerVersion.KOTLINC_1_3_72)
+ .withAllTargetVersions()
+ .build(),
BooleanUtils.values());
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
index f9381cb..de1e83c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataFirstToLatestTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -44,7 +45,10 @@
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- getKotlinTestParameters().withOldCompilersIfSet().withAllTargetVersions().build());
+ getKotlinTestParameters()
+ .withOldCompilersIfSet()
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .build());
}
public MetadataFirstToLatestTest(
@@ -86,15 +90,19 @@
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.writeToZip();
- AssertionError assertionError =
- assertThrows(
- AssertionError.class,
- () -> {
- runTest(kotlinParameters.getCompiler().getCompilerVersion(), libJar, stdLibJar);
- });
- assertThat(
- assertionError.getMessage(),
- containsString("compiled with an incompatible version of Kotlin"));
+ if (kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+ AssertionError assertionError =
+ assertThrows(
+ AssertionError.class,
+ () -> {
+ runTest(kotlinParameters.getCompiler().getCompilerVersion(), libJar, stdLibJar);
+ });
+ assertThat(
+ assertionError.getMessage(),
+ containsString("compiled with an incompatible version of Kotlin"));
+ } else {
+ runTest(kotlinParameters.getCompiler().getCompilerVersion(), libJar, stdLibJar);
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
index 4cbb2e0..9406052 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.allOf;
@@ -100,7 +101,7 @@
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.writeToZip();
- boolean expectingCompilationError = kotlinParameters.isOlderThanMinSupported() && !keepUnit;
+ boolean expectingCompilationError = kotlinParameters.isOlderThan(KOTLINC_1_4_20) && !keepUnit;
Path output =
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index 5739e90..1306816 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -135,8 +136,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
testForJvm()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
index b2e2631..a961f9e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -84,8 +85,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/anonymous_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
testForJvm()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
index e45b633..8282926 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -94,8 +95,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/flexible_upper_bound_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
testForJvm()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 2b316c9..ff9ff36 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -104,8 +105,8 @@
.addClasspathFiles(baseLibJar, libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index 472435a..eb588bb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -147,8 +148,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 19550d1..7a37728 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -109,8 +110,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(full || kotlinParameters.isOlderThanMinSupported());
- if (full || kotlinParameters.isOlderThanMinSupported()) {
+ .compile(full || kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (full || kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
@@ -166,8 +167,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index 3a8760a..02aa7a2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
@@ -106,8 +107,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(full || kotlinParameters.isOlderThanMinSupported());
- if (full || kotlinParameters.isOlderThanMinSupported()) {
+ .compile(full || kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (full || kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
@@ -172,8 +173,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index 875b475..9ae2cb0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -104,8 +105,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(full || kotlinParameters.isOlderThanMinSupported());
- if (full || kotlinParameters.isOlderThanMinSupported()) {
+ .compile(full || kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (full || kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
@@ -168,8 +169,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index fb2e952..435266f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -98,8 +99,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index cbbbbb0..f5be48a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -93,8 +94,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/nested_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index 557562c..86408fb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -87,8 +88,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index ad4f8a2..f93a28d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
@@ -96,8 +97,8 @@
.addSourceFiles(
getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
@@ -196,8 +197,8 @@
.addSourceFiles(
getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 2a69c5a..c352c26 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -85,8 +86,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index 84ee3ec..020c71d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -87,8 +88,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index afe617a..14fa2e1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -88,8 +89,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
@@ -138,8 +139,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index cfc0ea8..79af7bd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -138,8 +139,8 @@
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index 880bb14..36d4580 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
@@ -140,8 +141,8 @@
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main"))
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
testForJvm()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index b46dc08..8532cf9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_4_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
@@ -89,8 +90,8 @@
.addClasspathFiles(libJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .compile(kotlinParameters.isOlderThanMinSupported());
- if (kotlinParameters.isOlderThanMinSupported()) {
+ .compile(kotlinParameters.isOlderThan(KOTLINC_1_4_20));
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
return;
}
testForJvm()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index 664db57..3766300 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -98,7 +98,7 @@
ZipUtils.iter(
kotlinc.getKotlinStdlibJar().toString(),
((entry, input) -> {
- if (!entry.getName().endsWith(".class")) {
+ if (!entry.getName().endsWith(".class") || entry.getName().contains("module-info")) {
return;
}
final byte[] bytes = StreamUtils.streamToByteArrayClose(input);
diff --git a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
index 9893c44..328b485 100644
--- a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
+++ b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
@@ -5,19 +5,19 @@
package com.android.tools.r8.startup;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.Charset;
+import java.io.PrintWriter;
+import java.util.LinkedHashSet;
public class InstrumentationServerImpl extends InstrumentationServer {
private static final InstrumentationServerImpl INSTANCE = new InstrumentationServerImpl();
- private final StringBuilder builder = new StringBuilder();
-
// May be set to true by the instrumentation.
- private final boolean writeToLogcat = false;
- private final String logcatTag = "r8";
+ private static boolean writeToLogcat;
+ private static String logcatTag;
+
+ private final LinkedHashSet<String> lines = new LinkedHashSet<>();
private InstrumentationServerImpl() {}
@@ -33,25 +33,32 @@
getInstance().addLine('S' + descriptor);
}
- private synchronized void addLine(String line) {
+ private void addLine(String line) {
+ synchronized (lines) {
+ if (!lines.add(line)) {
+ return;
+ }
+ }
if (writeToLogcat) {
writeToLogcat(line);
- } else {
- builder.append(line).append('\n');
}
}
@Override
- public synchronized void writeToFile(File file) throws IOException {
- FileOutputStream stream = new FileOutputStream(file);
+ public void writeToFile(File file) throws IOException {
+ PrintWriter writer = new PrintWriter(file, "UTF-8");
try {
- stream.write(builder.toString().getBytes(Charset.forName("UTF-8")));
+ synchronized (lines) {
+ for (String line : lines) {
+ writer.println(line);
+ }
+ }
} finally {
- stream.close();
+ writer.close();
}
}
private void writeToLogcat(String line) {
- android.util.Log.v(logcatTag, line);
+ android.util.Log.i(logcatTag, line);
}
}
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index 4419055..d57a5ab 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -53,12 +52,13 @@
.setStartupConfiguration(
StartupConfiguration.builder()
.addStartupClass(
- StartupClass.<DexType>builder()
- .setReference(toDexType(Main.class, options.dexItemFactory()))
+ StartupClass.dexBuilder()
+ .setClassReference(
+ toDexType(Main.class, options.dexItemFactory()))
.build())
.addStartupClass(
- StartupClass.<DexType>builder()
- .setReference(
+ StartupClass.dexBuilder()
+ .setClassReference(
toDexType(AStartupClass.class, options.dexItemFactory()))
.build())
.build()))
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 6364cb0..6047b63 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -4,13 +4,24 @@
package com.android.tools.r8.startup;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.experimental.startup.StartupItem;
+import com.android.tools.r8.experimental.startup.StartupMethod;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.utils.StartupTestingUtils;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -22,48 +33,73 @@
public class StartupInstrumentationTest extends TestBase {
@Parameter(0)
+ public boolean logcat;
+
+ @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, logcat: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimesAndAllApiLevels().build());
}
@Test
- public void testD8() throws Exception {
- assumeTrue(parameters.isDexRuntime());
+ public void test() throws Exception {
+ Path out = temp.newFolder().toPath().resolve("out.txt").toAbsolutePath();
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
- .addOptionsModification(
- options -> options.getStartupOptions().setEnableStartupInstrumentation())
+ .applyIf(
+ logcat,
+ StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters),
+ StartupTestingUtils.enableStartupInstrumentationUsingFile(parameters))
+ .release()
.setMinApi(parameters.getApiLevel())
.compile()
- .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ logcat,
+ compileResult ->
+ compileResult.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp)))
+ .run(parameters.getRuntime(), Main.class, Boolean.toString(logcat), out.toString())
+ .applyIf(
+ logcat,
+ StartupTestingUtils.removeStartupListFromStdout(startupList::add),
+ runResult -> StartupTestingUtils.readStartupListFromFile(out, startupList::add))
.assertSuccessWithOutputLines(getExpectedOutput());
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepClassAndMembersRules(Main.class)
- .addOptionsModification(
- options -> options.getStartupOptions().setEnableStartupInstrumentation())
- .enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutput());
+ assertEquals(getExpectedStartupList(), startupList);
}
private static List<String> getExpectedOutput() {
- return ImmutableList.of(descriptor(Main.class), descriptor(AStartupClass.class), "foo");
+ return ImmutableList.of("foo");
+ }
+
+ private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ throws NoSuchMethodException {
+ return ImmutableList.of(
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(AStartupClass.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.methodFromMethod(AStartupClass.class.getDeclaredMethod("foo")))
+ .build());
}
static class Main {
- public static void main(String[] args) {
+ public static void main(String[] args) throws IOException {
+ boolean logcat = Boolean.parseBoolean(args[0]);
AStartupClass.foo();
+ if (!logcat) {
+ InstrumentationServer.getInstance().writeToFile(new File(args[1]));
+ }
}
// @Keep
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 884c1bf..5e25d36 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -13,15 +13,18 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.experimental.startup.StartupClass;
+import com.android.tools.r8.experimental.startup.StartupItem;
+import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -57,15 +60,16 @@
@Test
public void test() throws Exception {
- List<StartupClass<ClassReference>> startupList = new ArrayList<>();
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
- .apply(StartupTestingUtils.enableStartupInstrumentation(parameters))
+ .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .release()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
- .apply(StartupTestingUtils.removeStartupClassesFromStdout(startupList::add))
+ .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
.assertSuccessWithOutputLines(getExpectedOutput());
assertEquals(getExpectedStartupList(), startupList);
@@ -92,30 +96,60 @@
return ImmutableList.of("A", "B", "C");
}
- private List<StartupClass<ClassReference>> getExpectedStartupList() {
- ImmutableList.Builder<StartupClass<ClassReference>> builder = ImmutableList.builder();
+ private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ throws NoSuchMethodException {
+ ImmutableList.Builder<StartupMethod<ClassReference, MethodReference>> builder =
+ ImmutableList.builder();
builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(Main.class))
- .build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(A.class))
- .build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(B.class))
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(A.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
.build());
if (useLambda) {
builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(B.class))
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
.setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.instanceConstructor(B.class))
+ .setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.method(
+ Reference.classFromClass(B.class),
+ "accept",
+ ImmutableList.of(Reference.classFromClass(Object.class)),
+ null))
+ .setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
.build());
}
builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(C.class))
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(C.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
.build());
return builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 18ed52b..e1e3149 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -14,19 +14,23 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.experimental.startup.StartupClass;
+import com.android.tools.r8.experimental.startup.StartupItem;
+import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,15 +57,16 @@
@Test
public void test() throws Exception {
- List<StartupClass<ClassReference>> startupList = new ArrayList<>();
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
- .apply(StartupTestingUtils.enableStartupInstrumentation(parameters))
+ .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .release()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class)
- .apply(StartupTestingUtils.removeStartupClassesFromStdout(startupList::add))
+ .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
.assertSuccessWithOutputLines(getExpectedOutput());
assertEquals(getExpectedStartupList(), startupList);
@@ -88,30 +93,50 @@
return ImmutableList.of("A", "B", "C");
}
- private List<StartupClass<ClassReference>> getExpectedStartupList() {
- ImmutableList.Builder<StartupClass<ClassReference>> builder = ImmutableList.builder();
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(Main.class))
- .build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(A.class))
- .build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(B.class))
- .build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(B.class))
+ private List<StartupMethod<ClassReference, MethodReference>> getExpectedStartupList()
+ throws NoSuchMethodException {
+ return ImmutableList.of(
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(A.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("b")))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(B.class))
.setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.instanceConstructor(B.class))
+ .setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.method(
+ Reference.classFromClass(B.class), "run", Collections.emptyList(), null))
+ .setSynthetic()
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0")))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.classConstructor(C.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
.build());
- builder.add(
- StartupClass.<ClassReference>builder()
- .setReference(Reference.classFromClass(C.class))
- .build());
- return builder.build();
}
private List<ClassReference> getExpectedClassDataLayout(int virtualFile) {
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index dad4d44..ae31fe8 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.startup.utils;
import static com.android.tools.r8.TestBase.transformer;
+import static org.junit.Assert.fail;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestBuilder;
@@ -13,16 +14,23 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
+import com.android.tools.r8.experimental.startup.StartupItem;
+import com.android.tools.r8.experimental.startup.StartupOptions;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;
@@ -32,20 +40,27 @@
private static String startupInstrumentationTag = "startup";
- public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentation(
+ public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingFile(
TestParameters parameters) {
- return testBuilder -> enableStartupInstrumentation(testBuilder, parameters);
+ return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, false);
}
- public static void enableStartupInstrumentation(
- D8TestBuilder testBuilder, TestParameters parameters) throws IOException {
+ public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingLogcat(
+ TestParameters parameters) {
+ return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
+ }
+
+ private static void enableStartupInstrumentation(
+ D8TestBuilder testBuilder, TestParameters parameters, boolean logcat) throws IOException {
testBuilder
.addOptionsModification(
- options ->
- options
- .getStartupOptions()
- .setEnableStartupInstrumentation()
- .setStartupInstrumentationTag(startupInstrumentationTag))
+ options -> {
+ StartupOptions startupOptions =
+ options.getStartupOptions().setEnableStartupInstrumentation();
+ if (logcat) {
+ startupOptions.setStartupInstrumentationTag(startupInstrumentationTag);
+ }
+ })
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.addLibraryClassFileData(getTransformedAndroidUtilLog());
}
@@ -59,22 +74,37 @@
.writeToZip();
}
- public static ThrowingConsumer<D8TestRunResult, RuntimeException> removeStartupClassesFromStdout(
- Consumer<StartupClass<ClassReference>> startupClassConsumer) {
- return runResult -> removeStartupClassesFromStdout(runResult, startupClassConsumer);
+ public static void readStartupListFromFile(
+ Path path, Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer)
+ throws IOException {
+ StartupConfigurationParser.createReferenceParser()
+ .parseLines(
+ Files.readAllLines(path),
+ startupItemConsumer,
+ startupItemConsumer,
+ error -> fail("Unexpected parse error: " + error));
}
- public static void removeStartupClassesFromStdout(
- D8TestRunResult runResult, Consumer<StartupClass<ClassReference>> startupClassConsumer) {
+ public static ThrowingConsumer<D8TestRunResult, RuntimeException> removeStartupListFromStdout(
+ Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer) {
+ return runResult -> removeStartupListFromStdout(runResult, startupItemConsumer);
+ }
+
+ public static void removeStartupListFromStdout(
+ D8TestRunResult runResult,
+ Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer) {
+ StartupConfigurationParser<ClassReference, MethodReference, TypeReference> parser =
+ StartupConfigurationParser.createReferenceParser();
StringBuilder stdoutBuilder = new StringBuilder();
String startupDescriptorPrefix = "[" + startupInstrumentationTag + "] ";
for (String line : StringUtils.splitLines(runResult.getStdOut(), true)) {
if (line.startsWith(startupDescriptorPrefix)) {
- StartupClass.Builder<ClassReference> startupClassBuilder = StartupClass.builder();
String message = line.substring(startupDescriptorPrefix.length());
- message = StartupConfiguration.parseSyntheticFlag(message, startupClassBuilder);
- startupClassBuilder.setReference(Reference.classFromDescriptor(message));
- startupClassConsumer.accept(startupClassBuilder.build());
+ parser.parseLine(
+ message,
+ startupItemConsumer,
+ startupItemConsumer,
+ error -> fail("Unexpected parse error: " + error));
} else {
stdoutBuilder.append(line).append(System.lineSeparator());
}
@@ -83,29 +113,41 @@
}
public static void setStartupConfiguration(
- R8TestBuilder<?> testBuilder, List<StartupClass<ClassReference>> startupClasses) {
+ R8TestBuilder<?> testBuilder,
+ List<StartupItem<ClassReference, MethodReference, ?>> startupItems) {
testBuilder.addOptionsModification(
options -> {
DexItemFactory dexItemFactory = options.dexItemFactory();
- options
- .getStartupOptions()
- .setStartupConfiguration(
- StartupConfiguration.builder()
- .apply(
- builder ->
- startupClasses.forEach(
- startupClass ->
- builder.addStartupClass(
- StartupClass.<DexType>builder()
- .setFlags(startupClass.getFlags())
- .setReference(
- dexItemFactory.createType(
- startupClass.getReference().getDescriptor()))
- .build())))
- .build());
+ StartupConfiguration startupConfiguration =
+ StartupConfiguration.builder()
+ .apply(
+ builder ->
+ startupItems.forEach(
+ startupItem ->
+ builder.addStartupItem(
+ convertStartupItemToDex(startupItem, dexItemFactory))))
+ .build();
+ options.getStartupOptions().setStartupConfiguration(startupConfiguration);
});
}
+ private static StartupItem<DexType, DexMethod, ?> convertStartupItemToDex(
+ StartupItem<ClassReference, MethodReference, ?> startupItem, DexItemFactory dexItemFactory) {
+ return StartupItem.dexBuilder()
+ .applyIf(
+ startupItem.isStartupClass(),
+ builder ->
+ builder.setClassReference(
+ ClassReferenceUtils.toDexType(
+ startupItem.asStartupClass().getReference(), dexItemFactory)),
+ builder ->
+ builder.setMethodReference(
+ MethodReferenceUtils.toDexMethod(
+ startupItem.asStartupMethod().getReference(), dexItemFactory)))
+ .setFlags(startupItem.getFlags())
+ .build();
+ }
+
private static byte[] getTransformedAndroidUtilLog() throws IOException {
return transformer(Log.class).setClassDescriptor("Landroid/util/Log;").transform();
}