Merge commit '4e73b4a3fab11eaa523953de084645744b8c2e05' into dev-release
diff --git a/.gitignore b/.gitignore index 2e7f798..ceddaef 100644 --- a/.gitignore +++ b/.gitignore
@@ -266,8 +266,8 @@ tools/*/art-10.0.0.tar.gz tools/*/host/art-12.0.0-beta4 tools/*/host/art-12.0.0-beta4.tar.gz -tools/*/host/art-13-master -tools/*/host/art-13-master.tar.gz +tools/*/host/art-13-dev +tools/*/host/art-13-dev.tar.gz tools/*/art.tar.gz tools/*/dalvik tools/*/dalvik-4.0.4
diff --git a/README.md b/README.md index 2d24142..1621acb 100644 --- a/README.md +++ b/README.md
@@ -24,11 +24,11 @@ $ git clone https://r8.googlesource.com/r8 $ cd r8 - $ tools/gradle.py d8 r8 + $ tools/gradle.py r8 The `tools/gradle.py` script will bootstrap using depot_tools to download a version of gradle to use for building on the first run. This will produce -two jar files: `build/libs/d8.jar` and `build/libs/r8.jar`. +a jar file: `build/libs/r8.jar` which contains both R8 and D8. ## Running D8 @@ -45,11 +45,11 @@ Debug mode build: - $ java -jar build/libs/d8.jar --output out input.jar + $ java -cp build/libs/r8.jar com.android.tools.r8.D8 --output out input.jar Release mode build: - $ java -jar build/libs/d8.jar --release --output out input.jar + $ java -cp build/libs/r8.jar com.android.tools.r8.D8 --release --output out input.jar The full set of D8 options can be obtained by running the command line tool with the `--help` option.
diff --git a/build.gradle b/build.gradle index e5467f0..5f7b6d0 100644 --- a/build.gradle +++ b/build.gradle
@@ -369,7 +369,7 @@ "linux/art-9.0.0", "linux/art-10.0.0", "linux/host/art-12.0.0-beta4", - "linux/host/art-13-master", + "linux/host/art-13-dev", "linux/dalvik", "linux/dalvik-4.0.4", "${osString}/dx", @@ -929,15 +929,6 @@ outputs.file "${buildDir}/libs/r8_no_manifest.jar" } -task D8(type: Jar) { - dependsOn r8 - from zipTree(r8.outputs.files[0]) - archiveFileName = 'd8.jar' - manifest { - attributes 'Main-Class': 'com.android.tools.r8.D8' - } -} - def baseCompilerCommandLine(compiler, args = []) { // Execute r8 commands against a stable r8 with dependencies. // TODO(b/139725780): See if we can remove or lower the heap size (-Xmx8g).
diff --git a/src/library_desugar/java/android/os/Build.java b/src/library_desugar/java/android/os/Build.java new file mode 100644 index 0000000..908c94c --- /dev/null +++ b/src/library_desugar/java/android/os/Build.java
@@ -0,0 +1,11 @@ +// 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 android.os; + +public class Build { + public static class VERSION { + public static final int SDK_INT = 0; + } +}
diff --git a/src/library_desugar/java/android/os/StrictMode.java b/src/library_desugar/java/android/os/StrictMode.java new file mode 100644 index 0000000..5cd5ff1 --- /dev/null +++ b/src/library_desugar/java/android/os/StrictMode.java
@@ -0,0 +1,28 @@ +// 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 android.os; + +public class StrictMode { + + public static ThreadPolicy getThreadPolicy() { + return null; + } + + public static void setThreadPolicy(ThreadPolicy policy) {} + + public static class ThreadPolicy { + public static class Builder { + public Builder(ThreadPolicy policy) {} + + public Builder permitDiskReads() { + return null; + } + + public ThreadPolicy build() { + return null; + } + } + } +}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java new file mode 100644 index 0000000..cf36b80 --- /dev/null +++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileSystemProvider.java
@@ -0,0 +1,14 @@ +// 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 desugar.sun.nio.fs; + +import java.nio.file.spi.FileSystemProvider; + +public class DesugarDefaultFileSystemProvider { + + public static FileSystemProvider instance() { + return null; + } +}
diff --git a/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileTypeDetector.java b/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileTypeDetector.java new file mode 100644 index 0000000..916d6e0 --- /dev/null +++ b/src/library_desugar/java/desugar/sun/nio/fs/DesugarDefaultFileTypeDetector.java
@@ -0,0 +1,13 @@ +// 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 desugar.sun.nio.fs; + +import java.nio.file.spi.FileTypeDetector; + +public class DesugarDefaultFileTypeDetector { + public static FileTypeDetector create() { + return null; + } +}
diff --git a/src/library_desugar/java/j$/nio/file/Files.java b/src/library_desugar/java/j$/nio/file/Files.java new file mode 100644 index 0000000..2825d24 --- /dev/null +++ b/src/library_desugar/java/j$/nio/file/Files.java
@@ -0,0 +1,14 @@ +// 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; + +import java.io.IOException; +import java.nio.file.Path; + +public class Files { + public static String probeContentType(Path path) throws IOException { + return null; + } +}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java new file mode 100644 index 0000000..658c351 --- /dev/null +++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -0,0 +1,60 @@ +// 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.adapter; + +import android.os.Build.VERSION; +import android.os.StrictMode; +import android.os.StrictMode.ThreadPolicy; +import desugar.sun.nio.fs.DesugarDefaultFileSystemProvider; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.spi.FileSystemProvider; + +/** + * A hybrid file system provider adapter that delegates different implementations based on the + * 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() { + if (VERSION.SDK_INT >= 26) { + return FileSystems.getDefault().provider(); + } else { + try { + // In headless, android.os is absent so the following line will throw. + // We cannot set the ThreadPolicy in headless and it is irrelevant. + // If we are not in headless, the class will be found and we can set the thread policy. + Class.forName("android.os.Build"); + setThreadPolicy(); + } catch (ClassNotFoundException ignored) { + // Headless mode. + } + return DesugarDefaultFileSystemProvider.instance(); + } + } + + private static void setThreadPolicy() { + // The references to the android.os methods need to be outlined. + // TODO(b/207004118): Fix the strict mode allowlisting. + ThreadPolicy threadPolicy = StrictMode.getThreadPolicy(); + StrictMode.setThreadPolicy(new ThreadPolicy.Builder(threadPolicy).permitDiskReads().build()); + } + + private HybridFileSystemProvider() {} + + /** Returns the platform's default file system provider. */ + public static FileSystemProvider instance() { + return INSTANCE; + } + + /** Returns the platform's default file system. */ + public static FileSystem theFileSystem() { + return FILE_SYSTEM_INSTANCE; + } +}
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java new file mode 100644 index 0000000..1caf2f1 --- /dev/null +++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -0,0 +1,35 @@ +// 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.adapter; + +import android.os.Build.VERSION; +import desugar.sun.nio.fs.DesugarDefaultFileTypeDetector; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.spi.FileTypeDetector; + +/** + * A hybrid file type detector adapter that delegates different implementations based on the runtime + * environment. + */ +public final class HybridFileTypeDetector { + private HybridFileTypeDetector() {} + + public static FileTypeDetector create() { + if (VERSION.SDK_INT >= 26) { + return new PlatformFileTypeDetector(); + } else { + return DesugarDefaultFileTypeDetector.create(); + } + } + + static class PlatformFileTypeDetector extends java.nio.file.spi.FileTypeDetector { + @Override + public String probeContentType(Path path) throws IOException { + // Relies at runtime on java.nio.file.Files. + return j$.nio.file.Files.probeContentType(path); + } + } +}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json index c2cea80..6893b8f 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -6,20 +6,6 @@ "support_all_callbacks_from_library": true, "common_flags": [ { - "api_level_below_or_equal": 32, - "rewrite_prefix": { - "java.net.URLDecoder": "j$.net.URLDecoder", - "java.net.URLEncoder": "j$.net.URLEncoder", - "java.io.DesugarInputStream": "j$.io.DesugarInputStream" - }, - "retarget_method_with_emulated_dispatch": { - "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream" - }, - "amend_library_method": [ - "public long java.io.InputStream#transferTo(java.io.OutputStream)" - ] - }, - { "api_level_below_or_equal": 30, "rewrite_prefix": { "java.time.": "j$.time.", @@ -175,16 +161,6 @@ "java.util.OptionalInt": "java.util.OptionalConversions", "java.util.OptionalLong": "java.util.OptionalConversions" } - }, - { - "api_level_below_or_equal": 18, - "rewrite_prefix": { - "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter", - "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets" - }, - "retarget_method": { - "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter" - } } ], "program_flags": [ @@ -215,76 +191,32 @@ "long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong", "long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong" } - }, - { - "api_level_below_or_equal": 19, - "dont_retarget": [ - "android.support.multidex.MultiDexExtractor$ExtractedDex" - ] } ], "library_flags": [ { - "api_level_below_or_equal": 32, - "rewrite_prefix": { - "desugar.": "j$.desugar.", - "libcore.": "j$.libcore.", - "java.lang.Desugar": "j$.lang.Desugar", - "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner", - "sun.security.action.": "j$.sun.security.action." - } - }, - { "api_level_below_or_equal": 30, + "rewrite_prefix": { + "jdk.internal.": "j$.jdk.internal.", + "sun.security.action.": "j$.sun.security.action.", + "sun.misc.Desugar": "j$.sun.misc.Desugar" + }, "rewrite_derived_prefix": { "java.time.": { "j$.time.": "java.time." } - } - }, - { - "api_level_below_or_equal": 25, - "rewrite_prefix": { - "java.io.DesugarFile": "j$.io.DesugarFile", - "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel", - "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel", - "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler", - "java.nio.channels.Desugar": "j$.nio.channels.Desugar", - "java.nio.file.": "j$.nio.file.", - "jdk.internal.": "j$.jdk.internal.", - "sun.misc.Desugar": "j$.sun.misc.Desugar", - "sun.nio.cs.": "j$.sun.nio.cs.", - "sun.nio.fs.AbstractFileSystemProvider": "j$.sun.nio.fs.AbstractFileSystemProvider", - "sun.nio.fs.AbstractFileTypeDetector": "j$.sun.nio.fs.AbstractFileTypeDetector", - "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder", - "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView", - "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap", - "wrapper." : "j$.wrapper." - }, - "rewrite_derived_prefix": { - "java.nio.file.attribute.FileTime": { - "j$.nio.file.attribute.FileTime": "java.nio.file.attribute.FileTime" - }, - "java.io.": { - "__wrapper__.j$.io.": "j$.io.", - "__wrapper__.java.io.": "java.io." - }, - "java.nio.": { - "__wrapper__.j$.nio.": "j$.nio.", - "__wrapper__.java.nio.": "java.nio." - } - }, - "retarget_method": { - "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays" - }, - "retarget_method_with_emulated_dispatch": { - "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile" }, "backport": { "java.lang.DesugarDouble": "java.lang.Double", "java.lang.DesugarInteger": "java.lang.Integer", "java.lang.DesugarLong": "java.lang.Long", "java.lang.DesugarMath": "java.lang.Math" + } + }, + { + "api_level_below_or_equal": 25, + "retarget_method": { + "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays" }, "amend_library_method": [ "private static boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)" @@ -293,8 +225,6 @@ { "api_level_below_or_equal": 23, "rewrite_prefix": { - "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface", - "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel", "java.util.AbstractList": "j$.util.AbstractList", "java.util.CollSer": "j$.util.CollSer", "java.util.Comparators": "j$.util.Comparators",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json index 1985550..730a294 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -170,6 +170,7 @@ "api_level_below_or_equal": 30, "rewrite_prefix": { "j$.time.": "java.time.", + "jdk.internal.": "j$.jdk.internal.", "sun.misc.Desugar": "j$.sun.misc.Desugar", "sun.security.action.": "j$.sun.security.action." },
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json similarity index 83% copy from src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json copy to src/library_desugar/jdk11/desugar_jdk_libs_path.json index 57f7c3e..092b976 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -1,9 +1,9 @@ { - "identifier": "com.tools.android:desugar_jdk_libs_alternative_3:2.0.0", + "identifier": "com.tools.android:desugar_jdk_libs:2.0.0", "configuration_format_version": 100, "required_compilation_api_level": 30, "synthesized_library_classes_package_prefix": "j$.", - "support_all_callbacks_from_library": false, + "support_all_callbacks_from_library": true, "common_flags": [ { "api_level_below_or_equal": 32, @@ -52,9 +52,68 @@ } }, { + "api_level_below_or_equal": 25, + "rewrite_prefix": { + "java.io.DesugarFile": "j$.io.DesugarFile", + "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel", + "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel", + "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler", + "java.nio.file.": "j$.nio.file." + }, + "retarget_method_with_emulated_dispatch": { + "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile" + }, + "wrapper_conversion": [ + "java.nio.channels.AsynchronousChannel", + "java.nio.channels.CompletionHandler", + "java.nio.file.Path", + "java.nio.file.FileSystem", + "java.nio.file.WatchService", + "java.nio.file.WatchEvent$Kind", + "java.nio.file.WatchKey", + "java.nio.file.Watchable", + "java.nio.file.PathMatcher", + "java.nio.file.WatchEvent$Modifier", + "java.nio.file.attribute.UserPrincipalLookupService", + "java.nio.file.spi.FileSystemProvider", + "java.nio.file.AccessMode", + "java.nio.file.LinkOption", + "java.nio.file.CopyOption", + "java.nio.file.attribute.GroupPrincipal", + "java.nio.file.attribute.FileAttribute", + "java.nio.file.attribute.FileAttributeView", + "java.nio.file.attribute.UserPrincipal", + "java.nio.file.FileStore", + "java.nio.file.attribute.FileStoreAttributeView", + "java.nio.file.DirectoryStream$Filter", + "java.nio.file.DirectoryStream", + "java.nio.file.OpenOption", + "java.nio.file.attribute.BasicFileAttributes" + ], + "wrapper_conversion_excluding": { + "java.nio.channels.AsynchronousFileChannel": [ + "void java.nio.channels.AsynchronousFileChannel#lock(java.lang.Object, java.nio.channels.CompletionHandler)", + "java.util.concurrent.Future java.nio.channels.AsynchronousFileChannel#lock()", + "java.nio.channels.FileLock java.nio.channels.AsynchronousFileChannel#tryLock()" + ] + }, + "custom_conversion": { + "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions" + } + }, + { + "api_level_below_or_equal": 32, + "api_level_greater_or_equal": 24, + "rewrite_prefix": { + "java.util.stream.Collectors": "j$.util.stream.Collectors" + } + }, + { "api_level_below_or_equal": 23, "rewrite_prefix": { "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader", + "java.nio.channels.FileChannel": "j$.nio.channels.FileChannel", + "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel", "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics", "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics", "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics", @@ -69,6 +128,9 @@ "java.util.function.": "j$.util.function.", "java.util.stream.": "j$.util.stream." }, + "dont_rewrite_prefix": [ + "java.nio.channels.FileChannel$MapMode" + ], "maintain_prefix": [ "java.io.UncheckedIOException" ], @@ -110,6 +172,7 @@ "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet" }, "wrapper_conversion": [ + "java.nio.channels.SeekableByteChannel", "java.util.function.IntUnaryOperator", "java.util.function.BiFunction", "java.util.function.IntConsumer", @@ -164,6 +227,16 @@ "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[])", + "java.nio.channels.FileLock java.nio.channels.FileChannel#lock()", + "java.nio.channels.FileLock java.nio.channels.FileChannel#tryLock()", + "void java.nio.channels.spi.AbstractInterruptibleChannel#close()", + "boolean java.nio.channels.spi.AbstractInterruptibleChannel#isOpen()", + "void java.nio.channels.spi.AbstractInterruptibleChannel#begin()", + "void java.nio.channels.spi.AbstractInterruptibleChannel#end(boolean)" ] }, "custom_conversion": { @@ -198,6 +271,16 @@ } }, { + "api_level_below_or_equal": 25, + "rewrite_prefix": { + "java.io.DesugarFile": "j$.io.DesugarFile", + "java.nio.file.": "j$.nio.file." + }, + "retarget_method_with_emulated_dispatch": { + "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile" + } + }, + { "api_level_below_or_equal": 23, "retarget_method": { "int java.util.concurrent.atomic.AtomicInteger#accumulateAndGet(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger", @@ -245,12 +328,7 @@ { "api_level_below_or_equal": 25, "rewrite_prefix": { - "java.io.DesugarFile": "j$.io.DesugarFile", - "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel", - "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel", - "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler", "java.nio.channels.Desugar": "j$.nio.channels.Desugar", - "java.nio.file.": "j$.nio.file.", "jdk.internal.": "j$.jdk.internal.", "sun.misc.Desugar": "j$.sun.misc.Desugar", "sun.nio.cs.": "j$.sun.nio.cs.", @@ -259,27 +337,19 @@ "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder", "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView", "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap", - "wrapper." : "j$.wrapper." + "java.adapter" : "j$.adapter" }, "rewrite_derived_prefix": { "java.nio.file.attribute.FileTime": { "j$.nio.file.attribute.FileTime": "java.nio.file.attribute.FileTime" }, - "java.io.": { - "__wrapper__.j$.io.": "j$.io.", - "__wrapper__.java.io.": "java.io." - }, - "java.nio.": { - "__wrapper__.j$.nio.": "j$.nio.", - "__wrapper__.java.nio.": "java.nio." + "java.nio.file.Files": { + "j$.nio.file.Files": "java.nio.file.Files" } }, "retarget_method": { "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays" }, - "retarget_method_with_emulated_dispatch": { - "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile" - }, "backport": { "java.lang.DesugarDouble": "java.lang.Double", "java.lang.DesugarInteger": "java.lang.Integer", @@ -294,7 +364,6 @@ "api_level_below_or_equal": 23, "rewrite_prefix": { "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface", - "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel", "java.util.AbstractList": "j$.util.AbstractList", "java.util.CollSer": "j$.util.CollSer", "java.util.Comparators": "j$.util.Comparators",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json similarity index 98% rename from src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json rename to src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json index 57f7c3e..5064ba5 100644 --- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json +++ b/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json
@@ -262,9 +262,6 @@ "wrapper." : "j$.wrapper." }, "rewrite_derived_prefix": { - "java.nio.file.attribute.FileTime": { - "j$.nio.file.attribute.FileTime": "java.nio.file.attribute.FileTime" - }, "java.io.": { "__wrapper__.j$.io.": "j$.io.", "__wrapper__.java.io.": "java.io."
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index acbd677..fe2ed57 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -146,6 +146,41 @@ } /** + * Set a consumer for receiving the proguard-map content. + * + * <p>Note that when a proguard-map consumer is specified for a release build, the compiler will + * optimize the line-number information and obtaining a source-level stacktrace will require the + * use of a retrace tool exactly as is needed for programs built by R8. + * + * <p>Note that any subsequent call to this method or {@link #setProguardMapOutputPath} will + * override the previous setting. + * + * @param proguardMapConsumer Consumer to receive the content once produced. + */ + @Override + public Builder setProguardMapConsumer(StringConsumer proguardMapConsumer) { + return super.setProguardMapConsumer(proguardMapConsumer); + } + + /** + * Set an output destination to which proguard-map content should be written. + * + * <p>Note that when a proguard-map output is specified for a release build, the compiler will + * optimize the line-number information and obtaining a source-level stacktrace will require the + * use of a retrace tool exactly as is needed for programs built by R8. + * + * <p>This is a short-hand for setting a {@link StringConsumer.FileConsumer} using {@link + * #setProguardMapConsumer}. Note that any subsequent call to this method or {@link + * #setProguardMapConsumer} will override the previous setting. + * + * @param proguardMapOutput File-system path to write output at. + */ + @Override + public Builder setProguardMapOutputPath(Path proguardMapOutput) { + return super.setProguardMapOutputPath(proguardMapOutput); + } + + /** * Indicate if compilation is to intermediate results, i.e., intended for later merging. * * <p>When compiling to intermediate mode, the compiler will avoid sharing of synthetic items, @@ -376,6 +411,7 @@ getThreadCount(), getDumpInputFlags(), getMapIdProvider(), + proguardMapConsumer, factory); } } @@ -392,6 +428,7 @@ private final boolean enableMainDexListCheck; private final boolean minimalMainDex; private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules; + private final StringConsumer proguardMapConsumer; private final DexItemFactory factory; public static Builder builder() { @@ -460,6 +497,7 @@ int threadCount, DumpInputFlags dumpInputFlags, MapIdProvider mapIdProvider, + StringConsumer proguardMapConsumer, DexItemFactory factory) { super( inputApp, @@ -488,6 +526,7 @@ this.enableMainDexListCheck = enableMainDexListCheck; this.minimalMainDex = minimalMainDex; this.mainDexKeepRules = mainDexKeepRules; + this.proguardMapConsumer = proguardMapConsumer; this.factory = factory; } @@ -503,6 +542,7 @@ enableMainDexListCheck = true; minimalMainDex = false; mainDexKeepRules = null; + proguardMapConsumer = null; factory = null; } @@ -532,7 +572,11 @@ internal.setGlobalSyntheticsConsumer(globalSyntheticsConsumer); internal.desugarGraphConsumer = desugarGraphConsumer; internal.mainDexKeepRules = mainDexKeepRules; - internal.lineNumberOptimization = LineNumberOptimization.OFF; + internal.proguardMapConsumer = proguardMapConsumer; + internal.lineNumberOptimization = + !internal.debug && proguardMapConsumer != null + ? LineNumberOptimization.ON + : LineNumberOptimization.OFF; // Assert and fixup defaults. assert !internal.isShrinking();
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java index 9e729e0..719120f 100644 --- a/src/main/java/com/android/tools/r8/D8CommandParser.java +++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -30,6 +30,7 @@ "--lib", "--classpath", "--pg-map", + "--pg-map-output", MIN_API_FLAG, "--main-dex-rules", "--main-dex-list", @@ -131,6 +132,9 @@ + AndroidApiLevel.getDefault().getLevel() + ".", " --pg-map <file> # Use <file> as a mapping file for distribution.", + // TODO(b/183125319): Add help info once supported. + // " --pg-map-output <file> # Enable line optimization and output mapping to" + // +" <file>.", " --intermediate # Compile an intermediate result intended for later", " # merging.", " --file-per-class # Produce a separate dex file per class.", @@ -226,6 +230,8 @@ outputMode = OutputMode.ClassFile; } else if (arg.equals("--pg-map")) { builder.setProguardInputMapFile(Paths.get(nextArg)); + } else if (arg.equals("--pg-map-output")) { + builder.setProguardMapOutputPath(Paths.get(nextArg)); } else if (arg.equals("--output")) { if (outputPath != null) { builder.error(
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java deleted file mode 100644 index a7ac9e3..0000000 --- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright (c) 2018, 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; - -import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException; - -import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer; -import com.android.tools.r8.dex.ApplicationReader; -import com.android.tools.r8.dex.ApplicationWriter; -import com.android.tools.r8.dex.Marker; -import com.android.tools.r8.graph.AppInfo; -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexApplication; -import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.LazyLoadedDexApplication; -import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.naming.NamingLens; -import com.android.tools.r8.shaking.MainDexInfo; -import com.android.tools.r8.utils.ExceptionUtils; -import com.android.tools.r8.utils.FeatureClassMapping; -import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException; -import com.android.tools.r8.utils.InternalOptions; -import com.android.tools.r8.utils.InternalOptions.DesugarState; -import com.android.tools.r8.utils.ThreadUtils; -import com.android.tools.r8.utils.Timing; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -@Keep -public final class DexSplitterHelper { - - public static void run( - D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap) - throws CompilationFailedException { - ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED); - try { - ExceptionUtils.withCompilationHandler( - command.getReporter(), - () -> run(command, featureClassMapping, output, proguardMap, executor)); - } finally { - executor.shutdown(); - } - } - - public static void run( - D8Command command, - FeatureClassMapping featureClassMapping, - String output, - String proguardMap, - ExecutorService executor) - throws IOException { - InternalOptions options = command.getInternalOptions(); - options.desugarState = DesugarState.OFF; - options.enableMainDexListCheck = false; - options.ignoreMainDexMissingClasses = true; - options.minimalMainDex = false; - assert !options.isMinifying(); - options.inlinerOptions().enableInlining = false; - options.outline.enabled = false; - - try { - Timing timing = new Timing("DexSplitter"); - ApplicationReader applicationReader = - new ApplicationReader(command.getInputApp(), options, timing); - DexApplication app = applicationReader.read(executor); - MainDexInfo mainDexInfo = applicationReader.readMainDexClasses(app); - - List<Marker> markers = app.dexItemFactory.extractMarkers(); - - ClassNameMapper mapper = null; - if (proguardMap != null) { - mapper = ClassNameMapper.mapperFromFile(Paths.get(proguardMap)); - } - Map<String, LazyLoadedDexApplication.Builder> applications = - getDistribution(app, featureClassMapping, mapper); - for (Entry<String, LazyLoadedDexApplication.Builder> entry : applications.entrySet()) { - String feature = entry.getKey(); - timing.begin("Feature " + feature); - DexApplication featureApp = entry.getValue().build(); - assert !options.hasMethodsFilter(); - - // If this is the base, we add the main dex list. - AppInfo appInfo = - feature.equals(featureClassMapping.getBaseName()) - ? AppInfo.createInitialAppInfo(featureApp, mainDexInfo) - : AppInfo.createInitialAppInfo(featureApp); - AppView<AppInfo> appView = AppView.createForD8(appInfo); - - // Run d8 optimize to ensure jumbo strings are handled. - D8.optimize(appView, options, timing, executor); - - // We create a specific consumer for each split. - Path outputDir = Paths.get(output).resolve(entry.getKey()); - if (!Files.exists(outputDir)) { - Files.createDirectory(outputDir); - } - DexIndexedConsumer consumer = new DirectoryConsumer(outputDir); - - try { - new ApplicationWriter( - appView, - markers, - NamingLens.getIdentityLens(), - consumer) - .write(executor); - options.printWarnings(); - } finally { - consumer.finished(options.reporter); - } - timing.end(); - } - } catch (ExecutionException e) { - throw unwrapExecutionException(e); - } catch (FeatureMappingException e) { - options.reporter.error(e.getMessage()); - } finally { - options.signalFinishedToConsumers(); - } - } - - private static Map<String, LazyLoadedDexApplication.Builder> getDistribution( - DexApplication app, FeatureClassMapping featureClassMapping, ClassNameMapper mapper) - throws FeatureMappingException { - Map<String, LazyLoadedDexApplication.Builder> applications = new HashMap<>(); - for (DexProgramClass clazz : app.classes()) { - String clazzName = - mapper != null ? mapper.deobfuscateClassName(clazz.toString()) : clazz.toString(); - String feature = featureClassMapping.featureForClass(clazzName); - LazyLoadedDexApplication.Builder featureApplication = applications.get(feature); - if (featureApplication == null) { - featureApplication = DexApplication.builder(app.options, app.timing); - applications.put(feature, featureApplication); - } - featureApplication.addProgramClass(clazz); - } - return applications; - } - - public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8) - throws CompilationFailedException { - InternalOptions options = command.getInternalOptions(); - options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8; - D8.runForTesting(command.getInputApp(), options); - } -}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 888b78b..7bdb3b9 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -986,7 +986,11 @@ internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification); internal.synthesizedClassPrefix = synthesizedClassPrefix; // TODO(b/214382176): Enable all the time. - internal.loadAllClassDefinitions = !synthesizedClassPrefix.isEmpty(); + boolean l8Shrinking = !synthesizedClassPrefix.isEmpty(); + internal.loadAllClassDefinitions = l8Shrinking; + if (l8Shrinking) { + internal.apiModelingOptions().disableSubbingOfClasses(); + } internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer; // Set up the map and source file providers.
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java index 2d836ae..2e44e14 100644 --- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java +++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.bisect.Bisect; import com.android.tools.r8.cf.CfVerifierTool; import com.android.tools.r8.compatproguard.CompatProguard; -import com.android.tools.r8.dexsplitter.DexSplitter; import com.android.tools.r8.relocator.RelocatorCommandLine; import com.android.tools.r8.tracereferences.TraceReferences; import java.util.Arrays; @@ -41,9 +40,6 @@ case "dexsegments": DexSegments.main(shift(args)); break; - case "dexsplitter": - DexSplitter.main(shift(args)); - break; case "disasm": Disassemble.main(shift(args)); break;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java index 67e44d4..af0b133 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -215,4 +216,16 @@ FrameType frameType = FrameType.fromNumericType(type, dexItemFactory); frameBuilder.popAndDiscard(frameType, frameType).push(frameType); } + + @Override + public CfFrameState evaluate( + CfFrameState state, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., value1, value2 → + // ..., result + FrameType frameType = FrameType.fromNumericType(type, dexItemFactory); + return state.pop(appView, frameType, frameType).push(frameType); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java index 35a03f2..cdd43c2 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -20,6 +21,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -91,4 +93,14 @@ .popAndDiscardInitialized(dexItemFactory.objectArrayType) .push(dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java index dc6a732..5069f14 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,6 +25,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -136,4 +138,14 @@ frameBuilder.popAndDiscardInitialized(dexItemFactory.objectArrayType, dexItemFactory.intType); frameBuilder.push(FrameType.fromMemberType(type, dexItemFactory)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java index 39db93e..c51336f 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -127,4 +129,14 @@ .popAndDiscard(FrameType.fromMemberType(type, dexItemFactory)) .popAndDiscardInitialized(dexItemFactory.objectArrayType, dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java b/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java new file mode 100644 index 0000000..828a2f2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfAssignability.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.cf.code; + +import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.code.MemberType; + +public class CfAssignability { + + // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2. + public static boolean isAssignable(FrameType source, FrameType target, AppView<?> appView) { + if (target.isTop()) { + return true; + } + if (source.isTop()) { + return false; + } + if (source.isWide() != target.isWide()) { + return false; + } + if (target.isOneWord() || target.isTwoWord()) { + return true; + } + if (source.isUninitializedThis() && target.isUninitializedThis()) { + return true; + } + if (source.isUninitializedNew() && target.isUninitializedNew()) { + // TODO(b/168190134): Allow for picking the offset from the target if not set. + DexType uninitializedNewTypeSource = source.getUninitializedNewType(); + DexType uninitializedNewTypeTarget = target.getUninitializedNewType(); + return uninitializedNewTypeSource == null + || uninitializedNewTypeTarget == null + || uninitializedNewTypeSource == uninitializedNewTypeTarget; + } + // TODO(b/168190267): Clean-up the lattice. + DexItemFactory factory = appView.dexItemFactory(); + if (!source.isInitialized() + && target.isInitialized() + && target.getInitializedType() == factory.objectType) { + return true; + } + if (source.isInitialized() && target.isInitialized()) { + // Both are instantiated types and we resort to primitive tyoe/java type hierarchy checking. + return isAssignable(source.getInitializedType(), target.getInitializedType(), appView); + } + return false; + } + + // Rules found at https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2 + public static boolean isAssignable(DexType source, DexType target, AppView<?> appView) { + DexItemFactory factory = appView.dexItemFactory(); + source = byteCharShortOrBooleanToInt(source, factory); + target = byteCharShortOrBooleanToInt(target, factory); + if (source == target) { + return true; + } + if (source.isPrimitiveType() || target.isPrimitiveType()) { + return false; + } + // Both are now references - everything is assignable to object. + if (target == factory.objectType) { + return true; + } + // isAssignable(null, class(_, _)). + // isAssignable(null, arrayOf(_)). + if (source == DexItemFactory.nullValueType) { + return true; + } + if (target.isArrayType() != target.isArrayType()) { + return false; + } + if (target.isArrayType()) { + return isAssignable( + target.toArrayElementType(factory), target.toArrayElementType(factory), appView); + } + // TODO(b/166570659): Do a sub-type check that allows for missing classes in hierarchy. + return MemberType.fromDexType(source) == MemberType.fromDexType(target); + } + + private static DexType byteCharShortOrBooleanToInt(DexType type, DexItemFactory factory) { + // byte, char, short and boolean has verification type int. + if (type.isByteType() || type.isCharType() || type.isShortType() || type.isBooleanType()) { + return factory.intType; + } + return type; + } +}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java index edd6603..55dd90f 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -140,4 +142,14 @@ // ..., objectref frameBuilder.popAndDiscardInitialized(dexItemFactory.objectType).push(type); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java index 5b3c937..4ca69a0 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -25,6 +26,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -140,4 +142,14 @@ FrameType frameType = FrameType.fromNumericType(type, dexItemFactory); frameBuilder.popAndDiscard(frameType, frameType).push(dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java index abc6070..10240e5 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -147,4 +149,14 @@ // ..., value frameBuilder.push(dexItemFactory.classType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java index 3416ff7..666da46 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -31,6 +31,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.ConstantDynamic; @@ -234,4 +235,14 @@ // ..., value frameBuilder.push(dexItemFactory.classType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java index 48db7ef..2f79cea 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -111,4 +113,14 @@ // ..., value frameBuilder.push(dexItemFactory.methodHandleType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java index 855690f..0cc8d78 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -22,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -109,4 +111,14 @@ // ..., value frameBuilder.push(dexItemFactory.methodTypeType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java index d58b161..efcd063 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -20,6 +21,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -81,4 +83,14 @@ // ..., value frameBuilder.push(DexItemFactory.nullValueType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java index b1d9c5b..cee2f4a 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -21,6 +22,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; import org.objectweb.asm.MethodVisitor; @@ -238,4 +240,14 @@ // ..., value frameBuilder.push(type.toPrimitiveType().toDexType(dexItemFactory)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java index f03e9c7..4e4a325 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -20,6 +20,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; @@ -112,4 +113,15 @@ // ..., value frameBuilder.push(dexItemFactory.stringType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ... → + // ..., value + return frame.push(dexItemFactory.stringType); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java index cdc4235..5ab3742 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,6 +25,7 @@ import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -131,4 +133,14 @@ // ..., value frameBuilder.push(dexItemFactory.stringType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java index ce94d5d..80813e1 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -3,27 +3,17 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.cf.code; -import static com.android.tools.r8.utils.BiPredicateUtils.or; import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; import com.android.tools.r8.graph.DexField; 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.graph.GraphLens; import com.android.tools.r8.graph.InitClassLens; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.conversion.CfSourceCode; -import com.android.tools.r8.ir.conversion.CfState; -import com.android.tools.r8.ir.conversion.CfState.Slot; -import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; -import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -32,20 +22,20 @@ public abstract class CfFieldInstruction extends CfInstruction { - private final int opcode; private final DexField field; private final DexField declaringField; private static void specify(StructuralSpecification<CfFieldInstruction, ?> spec) { - spec.withInt(f -> f.opcode).withItem(f -> f.field).withItem(f -> f.declaringField); + spec.withInt(CfFieldInstruction::getOpcode) + .withItem(CfFieldInstruction::getField) + .withItem(CfFieldInstruction::getDeclaringField); } - public CfFieldInstruction(int opcode, DexField field) { - this(opcode, field, field); + public CfFieldInstruction(DexField field) { + this(field, field); } - public CfFieldInstruction(int opcode, DexField field, DexField declaringField) { - this.opcode = opcode; + public CfFieldInstruction(DexField field, DexField declaringField) { this.field = field; this.declaringField = declaringField; assert field.type == declaringField.type; @@ -70,13 +60,15 @@ return field; } - public int getOpcode() { - return opcode; + public DexField getDeclaringField() { + return declaringField; } + public abstract int getOpcode(); + @Override public int getCompareToId() { - return opcode; + return getOpcode(); } @Override @@ -86,15 +78,15 @@ } public boolean isFieldGet() { - return opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC; + return false; } public boolean isStaticFieldGet() { - return opcode == Opcodes.GETSTATIC; + return false; } public boolean isStaticFieldPut() { - return opcode == Opcodes.PUTSTATIC; + return false; } public abstract CfFieldInstruction createWithField(DexField field); @@ -124,7 +116,7 @@ String owner = namingLens.lookupInternalName(rewrittenField.holder); String name = namingLens.lookupName(rewrittenDeclaringField).toString(); String desc = namingLens.lookupDescriptor(rewrittenField.type).toString(); - visitor.visitFieldInsn(opcode, owner, name, desc); + visitor.visitFieldInsn(getOpcode(), owner, name, desc); } @Override @@ -141,92 +133,4 @@ public boolean canThrow() { return true; } - - @Override - public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { - DexType type = field.type; - switch (opcode) { - case Opcodes.GETSTATIC: - { - builder.addStaticGet(state.push(type).register, field); - break; - } - case Opcodes.PUTSTATIC: - { - Slot value = state.pop(); - builder.addStaticPut(value.register, field); - break; - } - case Opcodes.GETFIELD: - { - Slot object = state.pop(); - builder.addInstanceGet(state.push(type).register, object.register, field); - break; - } - case Opcodes.PUTFIELD: - { - Slot value = state.pop(); - Slot object = state.pop(); - builder.addInstancePut(value.register, object.register, field); - break; - } - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override - public ConstraintWithTarget inliningConstraint( - InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { - switch (opcode) { - case Opcodes.GETSTATIC: - return inliningConstraints.forStaticGet(field, context); - case Opcodes.PUTSTATIC: - return inliningConstraints.forStaticPut(field, context); - case Opcodes.GETFIELD: - return inliningConstraints.forInstanceGet(field, context); - case Opcodes.PUTFIELD: - return inliningConstraints.forInstancePut(field, context); - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } - - @Override - public void evaluate( - CfFrameVerificationHelper frameBuilder, - DexMethod context, - AppView<?> appView, - DexItemFactory dexItemFactory) { - switch (opcode) { - case Opcodes.GETFIELD: - // ..., objectref → - // ..., value - frameBuilder.popAndDiscardInitialized(field.holder).push(field.type); - return; - case Opcodes.GETSTATIC: - // ..., → - // ..., value - frameBuilder.push(field.type); - return; - case Opcodes.PUTFIELD: - // ..., objectref, value → - // ..., - frameBuilder - .popAndDiscardInitialized(field.type) - .pop( - field.holder, - or( - frameBuilder::isUninitializedThisAndTarget, - frameBuilder::isAssignableAndInitialized)); - return; - case Opcodes.PUTSTATIC: - // ..., value → - // ... - frameBuilder.popAndDiscardInitialized(field.type); - return; - default: - throw new Unreachable("Unexpected opcode " + opcode); - } - } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java index 7f7f08b..e7b8c99 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -6,6 +6,7 @@ import static org.objectweb.asm.Opcodes.F_NEW; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -26,6 +27,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap; import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap; @@ -377,12 +379,6 @@ return locals; } - // This is used from tests. As fastutils are repackaged and minified the method above is - // not available from tests which use fastutils in their original namespace. - public SortedMap<Integer, FrameType> getLocalsAsSortedMap() { - return locals; - } - public Deque<FrameType> getStack() { return stack; } @@ -504,6 +500,16 @@ frameBuilder.checkFrameAndSet(this); } + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + public CfFrame markInstantiated(FrameType uninitializedType, DexType initType) { if (uninitializedType.isInitialized()) { throw CfCodeStackMapValidatingException.error( @@ -520,7 +526,8 @@ return new CfFrame(newLocals, newStack); } - private FrameType getInitializedFrameType(FrameType unInit, FrameType other, DexType newType) { + public static FrameType getInitializedFrameType( + FrameType unInit, FrameType other, DexType newType) { assert !unInit.isInitialized(); if (other.isInitialized()) { return other;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java index c81abe4..aad0ba6 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
@@ -7,6 +7,7 @@ import static com.android.tools.r8.utils.BiPredicateUtils.or; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCodeStackMapValidatingException; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; @@ -27,17 +28,16 @@ public class CfFrameVerificationHelper { - private final CfFrame NO_FRAME = + private static final CfFrame NO_FRAME = new CfFrame( ImmutableInt2ReferenceSortedMap.<FrameType>builder().build(), ImmutableDeque.of()); - private final Deque<FrameType> throwStack; + private final AppView<?> appView; + private final DexItemFactory factory; private CfFrame currentFrame = NO_FRAME; private final DexType context; private final Map<CfLabel, CfFrame> stateMap; - private final BiPredicate<DexType, DexType> isJavaAssignable; - private final DexItemFactory factory; private final List<CfTryCatch> tryCatchRanges; private final int maxStackHeight; @@ -45,19 +45,17 @@ private final Set<CfLabel> tryCatchRangeLabels; public CfFrameVerificationHelper( + AppView<?> appView, DexType context, Map<CfLabel, CfFrame> stateMap, List<CfTryCatch> tryCatchRanges, - BiPredicate<DexType, DexType> isJavaAssignable, - DexItemFactory factory, int maxStackHeight) { + this.appView = appView; this.context = context; this.stateMap = stateMap; this.tryCatchRanges = tryCatchRanges; - this.isJavaAssignable = isJavaAssignable; - this.factory = factory; + this.factory = appView.dexItemFactory(); this.maxStackHeight = maxStackHeight; - throwStack = ImmutableDeque.of(FrameType.initialized(factory.throwableType)); // Compute all labels that marks a start or end to catch ranges. tryCatchRangeLabels = Sets.newIdentityHashSet(); for (CfTryCatch tryCatchRange : tryCatchRanges) { @@ -66,10 +64,6 @@ } } - public DexItemFactory factory() { - return factory; - } - public FrameType readLocal(int index, DexType expectedType) { checkFrameIsSet(); FrameType frameType = currentFrame.getLocals().get(index); @@ -197,15 +191,12 @@ // From the spec: the handler's exception class is assignable to the class Throwable. tryCatchRange.guards.forEach( guard -> { - if (!isJavaAssignable.test(guard, factory.throwableType)) { + if (!CfAssignability.isAssignable(guard, factory.throwableType, appView)) { throw CfCodeStackMapValidatingException.error( "Could not assign '" + guard.toSourceString() + "' to throwable."); } checkStackIsAssignable( - ImmutableDeque.of(FrameType.initialized(guard)), - destinationFrame.getStack(), - factory, - isJavaAssignable); + ImmutableDeque.of(FrameType.initialized(guard)), destinationFrame.getStack()); }); }); } @@ -237,8 +228,7 @@ if (destinationFrame == null) { throw CfCodeStackMapValidatingException.error("No frame for target catch range target"); } - checkLocalsIsAssignable( - currentFrame.getLocals(), destinationFrame.getLocals(), factory, isJavaAssignable); + checkLocalsIsAssignable(currentFrame.getLocals(), destinationFrame.getLocals()); } } } @@ -259,13 +249,7 @@ } public void checkFrame(Int2ReferenceSortedMap<FrameType> locals, Deque<FrameType> stack) { - checkIsAssignable( - currentFrame.getLocals(), - currentFrame.getStack(), - locals, - stack, - factory, - isJavaAssignable); + checkIsAssignable(currentFrame.getLocals(), currentFrame.getStack(), locals, stack); } public void setNoFrame() { @@ -290,7 +274,7 @@ if (!source.isInitialized()) { return false; } - return isJavaAssignable.test(source.getInitializedType(), target); + return CfAssignability.isAssignable(source.getInitializedType(), target, appView); } public void checkIsAssignable( @@ -303,31 +287,28 @@ } public void checkIsAssignable(FrameType source, FrameType target) { - if (!canBeAssigned(source, target, factory, isJavaAssignable)) { + if (!CfAssignability.isAssignable(source, target, appView)) { throw CfCodeStackMapValidatingException.error( "The expected type " + source + " is not assignable to " + target); } } // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.4. - public static void checkIsAssignable( + private void checkIsAssignable( Int2ReferenceSortedMap<FrameType> sourceLocals, Deque<FrameType> sourceStack, Int2ReferenceSortedMap<FrameType> destLocals, - Deque<FrameType> destStack, - DexItemFactory factory, - BiPredicate<DexType, DexType> isJavaAssignable) { - checkLocalsIsAssignable(sourceLocals, destLocals, factory, isJavaAssignable); - checkStackIsAssignable(sourceStack, destStack, factory, isJavaAssignable); + Deque<FrameType> destStack) { + checkLocalsIsAssignable(sourceLocals, destLocals); + checkStackIsAssignable(sourceStack, destStack); } - private static void checkLocalsIsAssignable( + private void checkLocalsIsAssignable( Int2ReferenceSortedMap<FrameType> sourceLocals, - Int2ReferenceSortedMap<FrameType> destLocals, - DexItemFactory factory, - BiPredicate<DexType, DexType> isJavaAssignable) { - final int localsLastKey = sourceLocals.isEmpty() ? -1 : sourceLocals.lastIntKey(); - final int otherLocalsLastKey = destLocals.isEmpty() ? -1 : destLocals.lastIntKey(); + Int2ReferenceSortedMap<FrameType> destLocals) { + // TODO(b/229826687): The tail of locals could have top(s) at destination but still be valid. + int localsLastKey = sourceLocals.isEmpty() ? -1 : sourceLocals.lastIntKey(); + int otherLocalsLastKey = destLocals.isEmpty() ? -1 : destLocals.lastIntKey(); if (localsLastKey < otherLocalsLastKey) { throw CfCodeStackMapValidatingException.error( "Source locals " @@ -336,11 +317,9 @@ + MapUtils.toString(destLocals)); } for (int i = 0; i < otherLocalsLastKey; i++) { - final FrameType sourceType = - sourceLocals.containsKey(i) ? sourceLocals.get(i) : FrameType.top(); - final FrameType destinationType = - destLocals.containsKey(i) ? destLocals.get(i) : FrameType.top(); - if (!canBeAssigned(sourceType, destinationType, factory, isJavaAssignable)) { + FrameType sourceType = sourceLocals.containsKey(i) ? sourceLocals.get(i) : FrameType.top(); + FrameType destinationType = destLocals.containsKey(i) ? destLocals.get(i) : FrameType.top(); + if (!CfAssignability.isAssignable(sourceType, destinationType, appView)) { throw CfCodeStackMapValidatingException.error( "Could not assign '" + MapUtils.toString(sourceLocals) @@ -357,11 +336,7 @@ } } - private static void checkStackIsAssignable( - Deque<FrameType> sourceStack, - Deque<FrameType> destStack, - DexItemFactory factory, - BiPredicate<DexType, DexType> isJavaAssignable) { + private void checkStackIsAssignable(Deque<FrameType> sourceStack, Deque<FrameType> destStack) { if (sourceStack.size() != destStack.size()) { throw CfCodeStackMapValidatingException.error( "Source stack " @@ -370,11 +345,11 @@ + Arrays.toString(destStack.toArray()) + " is not the same size"); } - final Iterator<FrameType> otherIterator = destStack.iterator(); + Iterator<FrameType> otherIterator = destStack.iterator(); int i = 0; for (FrameType sourceType : sourceStack) { - final FrameType destinationType = otherIterator.next(); - if (!canBeAssigned(sourceType, destinationType, factory, isJavaAssignable)) { + FrameType destinationType = otherIterator.next(); + if (!CfAssignability.isAssignable(sourceType, destinationType, appView)) { throw CfCodeStackMapValidatingException.error( "Could not assign '" + Arrays.toString(sourceStack.toArray()) @@ -391,46 +366,4 @@ i++; } } - - // Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2. - public static boolean canBeAssigned( - FrameType source, - FrameType target, - DexItemFactory factory, - BiPredicate<DexType, DexType> isJavaAssignable) { - if (target.isTop()) { - return true; - } - if (source.isTop()) { - return false; - } - if (source.isWide() != target.isWide()) { - return false; - } - if (target.isOneWord() || target.isTwoWord()) { - return true; - } - if (source.isUninitializedThis() && target.isUninitializedThis()) { - return true; - } - if (source.isUninitializedNew() && target.isUninitializedNew()) { - // TODO(b/168190134): Allow for picking the offset from the target if not set. - DexType uninitializedNewTypeSource = source.getUninitializedNewType(); - DexType uninitializedNewTypeTarget = target.getUninitializedNewType(); - return uninitializedNewTypeSource == null - || uninitializedNewTypeTarget == null - || uninitializedNewTypeSource == uninitializedNewTypeTarget; - } - // TODO(b/168190267): Clean-up the lattice. - if (!source.isInitialized() - && target.isInitialized() - && target.getInitializedType() == factory.objectType) { - return true; - } - if (source.isInitialized() && target.isInitialized()) { - // Both are instantiated types and we resort to primitive tyoe/java type hierarchy checking. - return isJavaAssignable.test(source.getInitializedType(), target.getInitializedType()); - } - return false; - } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java index fa3a8a5..ee4b0f2 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -19,7 +20,10 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -63,6 +67,14 @@ } @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return fn.apply(target, initialValue); + } + + @Override public void write( AppView<?> appView, ProgramMethod context, @@ -105,4 +117,14 @@ frameBuilder.checkTarget(target); frameBuilder.setNoFrame(); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java index 833ac44..d2fbccd 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,7 +24,10 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -66,6 +70,16 @@ return target; } + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return fn.apply(target, initialValue) + .ifContinueThen( + continuation -> fn.apply(fallthroughInstruction, continuation.getValueOrDefault(null))); + } + public int getOpcode() { switch (kind) { case EQ: @@ -146,4 +160,14 @@ : type.toPrimitiveType().toDexType(dexItemFactory)); frameBuilder.checkTarget(target); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java index c8129a6..f6d94b7 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,7 +25,10 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -67,6 +71,16 @@ return target; } + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return fn.apply(target, initialValue) + .ifContinueThen( + continuation -> fn.apply(fallthroughInstruction, continuation.getValueOrDefault(null))); + } + public int getOpcode() { switch (kind) { case EQ: @@ -149,4 +163,14 @@ frameBuilder.popAndDiscardInitialized(type, type); frameBuilder.checkTarget(target); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java index 1b5763c..046fca2 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -20,6 +21,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; import org.objectweb.asm.MethodVisitor; @@ -102,4 +104,14 @@ DexItemFactory dexItemFactory) { frameBuilder.readLocal(var, dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java index c307d45..4df1ecd 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -124,4 +126,14 @@ // ..., value frameBuilder.push(dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java index 2684c0d..df84afd 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
@@ -5,9 +5,22 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.code.CfOrDexInstanceFieldRead; +import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.conversion.CfSourceCode; +import com.android.tools.r8.ir.conversion.CfState; +import com.android.tools.r8.ir.conversion.CfState.Slot; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; +import com.android.tools.r8.ir.optimize.InliningConstraints; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import java.util.ListIterator; import org.objectweb.asm.Opcodes; @@ -18,7 +31,17 @@ } public CfInstanceFieldRead(DexField field, DexField declaringField) { - super(Opcodes.GETFIELD, field, declaringField); + super(field, declaringField); + } + + @Override + public int getOpcode() { + return Opcodes.GETFIELD; + } + + @Override + public boolean isFieldGet() { + return true; } @Override @@ -31,4 +54,37 @@ UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInstanceFieldReadInstruction(this); } + + @Override + public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { + Slot object = state.pop(); + builder.addInstanceGet(state.push(getField().getType()).register, object.register, getField()); + } + + @Override + public ConstraintWithTarget inliningConstraint( + InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { + return inliningConstraints.forInstanceGet(getField(), context); + } + + @Override + public void evaluate( + CfFrameVerificationHelper frameBuilder, + DexMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., objectref → + // ..., value + frameBuilder.popAndDiscardInitialized(getField().getHolderType()).push(getField().getType()); + } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java index c9138bb..03766dd 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -4,9 +4,24 @@ package com.android.tools.r8.cf.code; +import static com.android.tools.r8.utils.BiPredicateUtils.or; + +import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.conversion.CfSourceCode; +import com.android.tools.r8.ir.conversion.CfState; +import com.android.tools.r8.ir.conversion.CfState.Slot; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; +import com.android.tools.r8.ir.optimize.InliningConstraints; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import java.util.ListIterator; import org.objectweb.asm.Opcodes; @@ -17,7 +32,7 @@ } public CfInstanceFieldWrite(DexField field, DexField declaringField) { - super(Opcodes.PUTFIELD, field, declaringField); + super(field, declaringField); } @Override @@ -26,8 +41,53 @@ } @Override + public int getOpcode() { + return Opcodes.PUTFIELD; + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInstanceFieldWrite(getField()); } + + @Override + public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { + Slot value = state.pop(); + Slot object = state.pop(); + builder.addInstancePut(value.register, object.register, getField()); + } + + @Override + public ConstraintWithTarget inliningConstraint( + InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { + return inliningConstraints.forInstancePut(getField(), context); + } + + @Override + public void evaluate( + CfFrameVerificationHelper frameBuilder, + DexMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., objectref, value → + // ..., + frameBuilder + .popAndDiscardInitialized(getField().getType()) + .pop( + getField().getHolderType(), + or( + frameBuilder::isUninitializedThisAndTarget, + frameBuilder::isAssignableAndInitialized)); + } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java index 0469499..da48878 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -22,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -134,4 +136,14 @@ // ..., result frameBuilder.popAndDiscardInitialized(dexItemFactory.objectType).push(dexItemFactory.intType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java index 40b9737..7f449e6 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -25,8 +25,12 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; +import java.util.function.BiFunction; +import java.util.function.Consumer; import org.objectweb.asm.MethodVisitor; public abstract class CfInstruction implements CfOrDexInstruction { @@ -106,6 +110,26 @@ return null; } + public final void forEachNormalTarget( + Consumer<? super CfInstruction> consumer, CfInstruction fallthroughInstruction) { + traverseNormalTargets( + (target, ignore) -> { + consumer.accept(target); + return TraversalContinuation.doContinue(); + }, + fallthroughInstruction, + null); + } + + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + // The method is overridden in each jump instruction. + assert !isJump(); + return fn.apply(fallthroughInstruction, initialValue); + } + @Override public CfInstruction asCfInstruction() { return this; @@ -317,4 +341,7 @@ DexMethod context, AppView<?> appView, DexItemFactory dexItemFactory); + + public abstract CfFrameState evaluate( + CfFrameState frame, ProgramMethod context, AppView<?> appView, DexItemFactory dexItemFactory); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java index 8397bc6..c972e4e 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -32,6 +32,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.Arrays; @@ -337,6 +338,30 @@ } } + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., objectref, [arg1, [arg2 ...]] → + // ... [ returnType ] + // OR, for static method calls: + // ..., [arg1, [arg2 ...]] → + // ... + frame = frame.popInitialized(appView, method.getParameters().getBacking()); + if (opcode != Opcodes.INVOKESTATIC) { + frame = + opcode == Opcodes.INVOKESPECIAL && context.getDefinition().isInstanceInitializer() + ? frame.popAndInitialize(appView, method, context) + : frame.popInitialized(appView, method.getHolderType()); + } + if (method.getReturnType().isVoidType()) { + return frame; + } + return frame.push(method.getReturnType()); + } + private Type computeInvokeTypeForInvokeSpecial( AppView<?> appView, DexMethod method, ProgramMethod context, DexType originalHolder) { if (appView.dexItemFactory().isConstructor(method)) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java index a02060b..421902e 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -28,6 +29,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ArrayList; import java.util.List; @@ -178,4 +180,14 @@ frameBuilder.push(callSite.methodProto.returnType); } } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java index bc866cb..41e1063 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCodeStackMapValidatingException; @@ -21,6 +22,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; @@ -93,6 +95,16 @@ throw CfCodeStackMapValidatingException.error("Unexpected JSR/RET instruction"); } + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + public int getLocal() { return local; }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java index 3df1eba..4bd20a2 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -19,6 +20,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -102,4 +104,14 @@ DexItemFactory dexItemFactory) { frameBuilder.seenLabel(this); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java index 99a32da..889d4aa 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -22,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -139,4 +141,14 @@ ? dexItemFactory.objectType : type.toPrimitiveType().toDexType(dexItemFactory))); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java index 2407b10..a5ee7eb 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -197,4 +199,14 @@ } frameBuilder.popAndDiscard(value1Type, value2Type).push(value1Type); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java index 11df4ee..85ddc69 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -22,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -103,4 +105,14 @@ // ... frameBuilder.pop(FrameType.initialized(dexItemFactory.objectType)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java index 509e0cf..796a93f 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -22,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; @@ -140,4 +142,14 @@ } frameBuilder.push(type); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java index da12fc5..9c6a7f8 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -129,4 +131,14 @@ FrameType frameType = FrameType.fromNumericType(type, dexItemFactory); frameBuilder.popAndDiscard(frameType).push(frameType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java index b4c070a..0fde2a7 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -123,4 +124,15 @@ // ..., objectref frameBuilder.push(FrameType.uninitializedNew(new CfLabel(), type)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ... → + // ..., objectref + return frame.push(FrameType.uninitializedNew(new CfLabel(), type)); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java index 5149789..ba81a58 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,6 +25,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; @@ -174,4 +176,14 @@ assert type.isArrayType(); frameBuilder.popAndDiscardInitialized(dexItemFactory.intType).push(type); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java index 0c75328..8683ddd 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,6 +25,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ListIterator; import org.objectweb.asm.MethodVisitor; @@ -126,4 +128,14 @@ // ..., objectref frameBuilder.push(FrameType.initialized(type)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java index f77241e..e8b3a2e 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -19,6 +20,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -83,4 +85,14 @@ DexItemFactory dexItemFactory) { // This is an actual Nop. } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java index b8fe9c1..0341487 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -201,4 +203,14 @@ .popAndDiscard(FrameType.fromNumericType(from, dexItemFactory)) .push(FrameType.fromNumericType(to, dexItemFactory)); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java index 6f82c07..155b17d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -20,6 +21,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; @@ -116,4 +118,14 @@ DexItemFactory dexItemFactory) { // This is a no-op. } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java index f8fad92..4a9fd91 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfRecordFieldValues.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -23,6 +24,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -119,4 +121,14 @@ } frameBuilder.push(dexItemFactory.objectArrayType); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java index d06db87..27b73f6 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -22,7 +23,10 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -72,6 +76,14 @@ } @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return TraversalContinuation.doContinue(initialValue); + } + + @Override public boolean isJump() { return true; } @@ -121,4 +133,14 @@ frameBuilder.popAndDiscardInitialized(context.getReturnType()); frameBuilder.setNoFrame(); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java index d492b04..a61642f 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -19,13 +19,24 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class CfReturnVoid extends CfInstruction { @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return TraversalContinuation.doContinue(initialValue); + } + + @Override public boolean isJump() { return true; } @@ -93,4 +104,13 @@ DexItemFactory dexItemFactory) { frameBuilder.setNoFrame(); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + return CfFrameState.bottom(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java index 366033d..5f5e5a9 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -24,6 +25,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -358,7 +360,6 @@ DexMethod context, AppView<?> appView, DexItemFactory dexItemFactory) { - switch (opcode) { case Pop: // ..., value → @@ -513,4 +514,61 @@ throw new Unreachable("Invalid opcode for CfStackInstruction"); } } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + switch (opcode) { + case Pop: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Pop2: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Dup: + // ..., value → + // ..., value, value + return frame.pop( + appView, FrameType.oneWord(), frameType -> frame.push(frameType).push(frameType)); + case DupX1: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case DupX2: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Dup2: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Dup2X1: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Dup2X2: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + case Swap: + { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + default: + throw new Unreachable("Invalid opcode for CfStackInstruction"); + } + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java index 30715e1..3950a9c 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
@@ -5,20 +5,31 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.code.CfOrDexStaticFieldRead; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.conversion.CfSourceCode; +import com.android.tools.r8.ir.conversion.CfState; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; +import com.android.tools.r8.ir.optimize.InliningConstraints; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import java.util.ListIterator; import org.objectweb.asm.Opcodes; public class CfStaticFieldRead extends CfFieldInstruction implements CfOrDexStaticFieldRead { public CfStaticFieldRead(DexField field) { - super(Opcodes.GETSTATIC, field); + super(field); } public CfStaticFieldRead(DexField field, DexField declaringField) { - super(Opcodes.GETSTATIC, field, declaringField); + super(field, declaringField); } @Override @@ -27,8 +38,56 @@ } @Override + public int getOpcode() { + return Opcodes.GETSTATIC; + } + + @Override + public boolean isFieldGet() { + return true; + } + + @Override + public boolean isStaticFieldGet() { + return true; + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerStaticFieldReadInstruction(this); } + + @Override + public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { + builder.addStaticGet(state.push(getField().getType()).register, getField()); + } + + @Override + public ConstraintWithTarget inliningConstraint( + InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { + return inliningConstraints.forStaticGet(getField(), context); + } + + @Override + public void evaluate( + CfFrameVerificationHelper frameBuilder, + DexMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., → + // ..., value + frameBuilder.push(getField().getType()); + } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., → + // ..., value + return frame.push(getField().getType()); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java index 24b5e6d..26114d8 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
@@ -4,20 +4,33 @@ package com.android.tools.r8.cf.code; +import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.conversion.CfSourceCode; +import com.android.tools.r8.ir.conversion.CfState; +import com.android.tools.r8.ir.conversion.CfState.Slot; +import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; +import com.android.tools.r8.ir.optimize.InliningConstraints; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import java.util.ListIterator; import org.objectweb.asm.Opcodes; public class CfStaticFieldWrite extends CfFieldInstruction { public CfStaticFieldWrite(DexField field) { - super(Opcodes.PUTSTATIC, field); + super(field); } public CfStaticFieldWrite(DexField field, DexField declaringField) { - super(Opcodes.PUTSTATIC, field, declaringField); + super(field, declaringField); } @Override @@ -26,8 +39,51 @@ } @Override + public int getOpcode() { + return Opcodes.PUTSTATIC; + } + + @Override + public boolean isStaticFieldPut() { + return true; + } + + @Override void internalRegisterUse( UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerStaticFieldWrite(getField()); } + + @Override + public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { + Slot value = state.pop(); + builder.addStaticPut(value.register, getField()); + } + + @Override + public ConstraintWithTarget inliningConstraint( + InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { + return inliningConstraints.forStaticPut(getField(), context); + } + + @Override + public void evaluate( + CfFrameVerificationHelper frameBuilder, + DexMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // ..., value → + // ... + frameBuilder.popAndDiscardInitialized(getField().getType()); + } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java index 616ed84..e095dc8 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -25,6 +26,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; import com.android.tools.r8.utils.structural.CompareToVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -172,4 +174,14 @@ throw new Unreachable("Unexpected type " + type); } } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java index a86bcd1..ea40b51 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -21,9 +22,13 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; +import com.android.tools.r8.utils.TraversalUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; import it.unimi.dsi.fastutil.ints.IntArrayList; import java.util.List; +import java.util.function.BiFunction; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -47,6 +52,16 @@ } @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return TraversalUtils.traverseIterable(targets, fn, initialValue) + .ifContinueThen( + continuation -> fn.apply(defaultTarget, continuation.getValueOrDefault(null))); + } + + @Override public int getCompareToId() { return kind == Kind.LOOKUP ? Opcodes.LOOKUPSWITCH : Opcodes.TABLESWITCH; } @@ -171,4 +186,14 @@ } frameBuilder.setNoFrame(); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java index 6e5eb78..d0aa3f8 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.cf.code; import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.CfCompareHelper; @@ -20,13 +21,24 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState; +import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.structural.CompareToVisitor; +import java.util.function.BiFunction; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class CfThrow extends CfInstruction { @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets( + BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn, + CfInstruction fallthroughInstruction, + CT initialValue) { + return TraversalContinuation.doContinue(initialValue); + } + + @Override public boolean isJump() { return true; } @@ -104,4 +116,14 @@ // The exception edges are verified in CfCode since this is a throwing instruction. frameBuilder.setNoFrame(); } + + @Override + public CfFrameState evaluate( + CfFrameState frame, + ProgramMethod context, + AppView<?> appView, + DexItemFactory dexItemFactory) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java index 2f821be..22aac9a 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.utils.structural.CompareToVisitor; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; public class CfTryCatch { public final CfLabel start; @@ -28,6 +29,22 @@ assert verifyAllNonNull(guards); } + public void forEachTarget(Consumer<CfLabel> consumer) { + targets.forEach(consumer); + } + + public CfLabel getStart() { + return start; + } + + public CfLabel getEnd() { + return end; + } + + public List<CfLabel> getTargets() { + return targets; + } + private static boolean verifyAllNonNull(List<DexType> types) { for (DexType type : types) { assert type != null;
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java deleted file mode 100644 index 4ee416c..0000000 --- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java +++ /dev/null
@@ -1,377 +0,0 @@ -// Copyright (c) 2018, 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.dexsplitter; - -import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.D8Command; -import com.android.tools.r8.DexIndexedConsumer; -import com.android.tools.r8.DexSplitterHelper; -import com.android.tools.r8.Diagnostic; -import com.android.tools.r8.DiagnosticsHandler; -import com.android.tools.r8.Keep; -import com.android.tools.r8.origin.PathOrigin; -import com.android.tools.r8.utils.AbortException; -import com.android.tools.r8.utils.ExceptionDiagnostic; -import com.android.tools.r8.utils.ExceptionUtils; -import com.android.tools.r8.utils.FeatureClassMapping; -import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException; -import com.android.tools.r8.utils.OptionsParsing; -import com.android.tools.r8.utils.OptionsParsing.ParseContext; -import com.android.tools.r8.utils.StringDiagnostic; -import com.android.tools.r8.utils.ZipUtils; -import com.google.common.collect.ImmutableList; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -@Keep -public final class DexSplitter { - - private static final String DEFAULT_OUTPUT_DIR = "output"; - private static final String DEFAULT_BASE_NAME = "base"; - - private static final boolean PRINT_ARGS = false; - - public static class FeatureJar { - private String jar; - private String outputName; - - public FeatureJar(String jar, String outputName) { - this.jar = jar; - this.outputName = outputName; - } - - public FeatureJar(String jar) { - this(jar, featureNameFromJar(jar)); - } - - public String getJar() { - return jar; - } - - public String getOutputName() { - return outputName; - } - - private static String featureNameFromJar(String jar) { - Path jarPath = Paths.get(jar); - String featureName = jarPath.getFileName().toString(); - if (featureName.endsWith(".jar") || featureName.endsWith(".zip")) { - featureName = featureName.substring(0, featureName.length() - 4); - } - return featureName; - } - } - - private static class ZipFileOrigin extends PathOrigin { - - public ZipFileOrigin(Path path) { - super(path); - } - - @Override - public String part() { - return "splitting of file '" + super.part() + "'"; - } - } - - @Keep - public static final class Options { - private final DiagnosticsHandler diagnosticsHandler; - private List<String> inputArchives = new ArrayList<>(); - private List<FeatureJar> featureJars = new ArrayList<>(); - private List<String> baseJars = new ArrayList<>(); - private String baseOutputName = DEFAULT_BASE_NAME; - private String output = DEFAULT_OUTPUT_DIR; - private String featureSplitMapping; - private String proguardMap; - private String mainDexList; - private boolean splitNonClassResources = false; - - public Options() { - this(new DiagnosticsHandler() {}); - } - - public Options(DiagnosticsHandler diagnosticsHandler) { - this.diagnosticsHandler = diagnosticsHandler; - } - - public DiagnosticsHandler getDiagnosticsHandler() { - return diagnosticsHandler; - } - - public String getMainDexList() { - return mainDexList; - } - - public void setMainDexList(String mainDexList) { - this.mainDexList = mainDexList; - } - - public String getOutput() { - return output; - } - - public void setOutput(String output) { - this.output = output; - } - - public String getFeatureSplitMapping() { - return featureSplitMapping; - } - - public void setFeatureSplitMapping(String featureSplitMapping) { - this.featureSplitMapping = featureSplitMapping; - } - - public String getProguardMap() { - return proguardMap; - } - - public void setProguardMap(String proguardMap) { - this.proguardMap = proguardMap; - } - - public String getBaseOutputName() { - return baseOutputName; - } - - public void setBaseOutputName(String baseOutputName) { - this.baseOutputName = baseOutputName; - } - - public void addInputArchive(String inputArchive) { - inputArchives.add(inputArchive); - } - - public void addBaseJar(String baseJar) { - baseJars.add(baseJar); - } - - private void addFeatureJar(FeatureJar featureJar) { - featureJars.add(featureJar); - } - - public void addFeatureJar(String jar) { - featureJars.add(new FeatureJar(jar)); - } - - public void addFeatureJar(String jar, String outputName) { - featureJars.add(new FeatureJar(jar, outputName)); - } - - public void setSplitNonClassResources(boolean value) { - splitNonClassResources = value; - } - - public ImmutableList<String> getInputArchives() { - return ImmutableList.copyOf(inputArchives); - } - - ImmutableList<FeatureJar> getFeatureJars() { - return ImmutableList.copyOf(featureJars); - } - - ImmutableList<String> getBaseJars() { - return ImmutableList.copyOf(baseJars); - } - - // Shorthand error messages. - public Diagnostic error(String msg) { - StringDiagnostic error = new StringDiagnostic(msg); - diagnosticsHandler.error(error); - return error; - } - } - - /** - * Parse a feature jar argument and return the corresponding FeatureJar representation. - * Default to use the name of the jar file if the argument contains no ':', if the argument - * contains ':', then use the value after the ':' as the name. - * @param argument - */ - private static FeatureJar parseFeatureJarArgument(String argument) { - if (argument.contains(":")) { - String[] parts = argument.split(":"); - if (parts.length > 2) { - throw new RuntimeException("--feature-jar argument contains more than one :"); - } - return new FeatureJar(parts[0], parts[1]); - } - return new FeatureJar(argument); - } - - private static Options parseArguments(String[] args) { - Options options = new Options(); - ParseContext context = new ParseContext(args); - while (context.head() != null) { - List<String> inputs = OptionsParsing.tryParseMulti(context, "--input"); - if (inputs != null) { - inputs.forEach(options::addInputArchive); - continue; - } - List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar"); - if (featureJars != null) { - featureJars.forEach((feature) -> options.addFeatureJar(parseFeatureJarArgument(feature))); - continue; - } - List<String> baseJars = OptionsParsing.tryParseMulti(context, "--base-jar"); - if (baseJars != null) { - baseJars.forEach(options::addBaseJar); - continue; - } - String output = OptionsParsing.tryParseSingle(context, "--output", "-o"); - if (output != null) { - options.setOutput(output); - continue; - } - - String mainDexList= OptionsParsing.tryParseSingle(context, "--main-dex-list", null); - if (mainDexList!= null) { - options.setMainDexList(mainDexList); - continue; - } - - String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null); - if (proguardMap != null) { - options.setProguardMap(proguardMap); - continue; - } - String baseOutputName = OptionsParsing.tryParseSingle(context, "--base-output-name", null); - if (baseOutputName != null) { - options.setBaseOutputName(baseOutputName); - continue; - } - String featureSplit = OptionsParsing.tryParseSingle(context, "--feature-splits", null); - if (featureSplit != null) { - options.setFeatureSplitMapping(featureSplit); - continue; - } - Boolean b = OptionsParsing.tryParseBoolean(context, "--split-non-class-resources"); - if (b != null) { - options.setSplitNonClassResources(b); - continue; - } - throw new RuntimeException(String.format("Unknown options: '%s'.", context.head())); - } - return options; - } - - private static FeatureClassMapping createFeatureClassMapping(Options options) - throws FeatureMappingException { - if (options.getFeatureSplitMapping() != null) { - return FeatureClassMapping.fromSpecification( - Paths.get(options.getFeatureSplitMapping()), options.getDiagnosticsHandler()); - } - assert !options.getFeatureJars().isEmpty(); - return FeatureClassMapping.Internal.fromJarFiles(options.getFeatureJars(), - options.getBaseJars(), options.getBaseOutputName(), options.getDiagnosticsHandler()); - } - - private static void run(String[] args) - throws CompilationFailedException, FeatureMappingException { - Options options = parseArguments(args); - run(options); - } - - public static void run(Options options) - throws FeatureMappingException, CompilationFailedException { - Diagnostic error = null; - if (options.getInputArchives().isEmpty()) { - error = options.error("Need at least one --input"); - } - if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) { - error = options.error("You must supply a feature split mapping or feature jars"); - } - if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) { - error = options.error("You can't supply both a feature split mapping and feature jars"); - } - if (error != null) { - throw new AbortException(error); - } - - D8Command.Builder builder = D8Command.builder(options.diagnosticsHandler); - - - for (String s : options.inputArchives) { - builder.addProgramFiles(Paths.get(s)); - } - // We set the actual consumer on the ApplicationWriter when we have calculated the distribution - // since we don't yet know the distribution. - builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer()); - if (options.getMainDexList() != null) { - builder.addMainDexListFiles(Paths.get(options.getMainDexList())); - } - - FeatureClassMapping featureClassMapping = createFeatureClassMapping(options); - - DexSplitterHelper.run( - builder.build(), featureClassMapping, options.getOutput(), options.getProguardMap()); - - if (options.splitNonClassResources) { - splitNonClassResources(options, featureClassMapping); - } - } - - private static void splitNonClassResources(Options options, - FeatureClassMapping featureClassMapping) { - for (String s : options.inputArchives) { - try (ZipFile zipFile = new ZipFile(s, StandardCharsets.UTF_8)) { - Enumeration<? extends ZipEntry> entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - String name = entry.getName(); - if (!ZipUtils.isDexFile(name) && !ZipUtils.isClassFile(name)) { - String feature = featureClassMapping.featureForNonClass(name); - Path outputDir = Paths.get(options.getOutput()).resolve(feature); - try (InputStream stream = zipFile.getInputStream(entry)) { - Path outputFile = outputDir.resolve(name); - Path parent = outputFile.getParent(); - if (parent != null) { - Files.createDirectories(parent); - } - Files.copy(stream, outputFile); - } - } - } - } catch (IOException e) { - ExceptionDiagnostic error = new ExceptionDiagnostic(e, new ZipFileOrigin(Paths.get(s))); - options.getDiagnosticsHandler().error(error); - throw new AbortException(error); - } - } - } - - public static void main(String[] args) { - if (PRINT_ARGS) { - printArgs(args); - } - ExceptionUtils.withMainProgramHandler( - () -> { - try { - run(args); - } catch (FeatureMappingException e) { - // TODO(ricow): Report feature mapping errors via the reporter. - throw new RuntimeException("Splitting failed: " + e.getMessage()); - } - }); - } - - private static void printArgs(String[] args) { - System.err.printf("r8.DexSplitter"); - for (String s : args) { - System.err.printf(" %s", s); - } - System.err.println(""); - } -}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java index 81979c8..e5e4459 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -592,7 +592,7 @@ // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). public DexEncodedMethod lookupStaticTarget(DexMethod method, DexProgramClass context) { assert checkIfObsolete(); - return unsafeResolveMethodDueToDexFormat(method).lookupInvokeStaticTarget(context, this); + return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeStaticTarget(context, this); } // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). @@ -613,7 +613,7 @@ // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). public DexClassAndMethod lookupSuperTarget(DexMethod method, DexProgramClass context) { assert checkIfObsolete(); - return unsafeResolveMethodDueToDexFormat(method).lookupInvokeSuperTarget(context, this); + return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeSuperTarget(context, this); } // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). @@ -632,7 +632,7 @@ // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). public DexEncodedMethod lookupDirectTarget(DexMethod method, DexProgramClass context) { assert checkIfObsolete(); - return unsafeResolveMethodDueToDexFormat(method).lookupInvokeDirectTarget(context, this); + return unsafeResolveMethodDueToDexFormatLegacy(method).lookupInvokeDirectTarget(context, this); } // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). @@ -646,32 +646,129 @@ * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the * kind of a method reference. */ - public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) { + public MethodResolutionResult unsafeResolveMethodDueToDexFormatLegacy(DexMethod method) { assert checkIfObsolete(); return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) .unsafeResolveMethodDueToDexFormat(method); } + public MethodResolutionResult resolveMethodLegacy(DexMethod invokedMethod, boolean isInterface) { + assert checkIfObsolete(); + return resolveMethodOnLegacy(invokedMethod.getHolderType(), invokedMethod, isInterface); + } + + public MethodResolutionResult resolveMethodOnLegacy(DexClass clazz, DexMethod method) { + assert checkIfObsolete(); + return clazz.isInterface() + ? resolveMethodOnInterfaceLegacy(clazz, method) + : resolveMethodOnClassLegacy(clazz, method); + } + + public MethodResolutionResult resolveMethodOnLegacy( + DexClass clazz, DexMethodSignature methodSignature) { + assert checkIfObsolete(); + return clazz.isInterface() + ? resolveMethodOnInterfaceLegacy(clazz, methodSignature) + : resolveMethodOnClassLegacy(clazz, methodSignature); + } + + public MethodResolutionResult resolveMethodOnLegacy( + DexType holder, DexMethod method, boolean isInterface) { + assert checkIfObsolete(); + return isInterface + ? resolveMethodOnInterfaceLegacy(holder, method) + : resolveMethodOnClassLegacy(holder, method); + } + + public MethodResolutionResult resolveMethodOnClassHolderLegacy(DexMethod method) { + assert checkIfObsolete(); + return resolveMethodOnClassLegacy(method.getHolderType(), method); + } + + public MethodResolutionResult resolveMethodOnClassLegacy(DexType holder, DexMethod method) { + assert checkIfObsolete(); + return resolveMethodOnClassLegacy(holder, method.getProto(), method.getName()); + } + + public MethodResolutionResult resolveMethodOnClassLegacy( + DexType holder, DexMethodSignature signature) { + assert checkIfObsolete(); + return resolveMethodOnClassLegacy(holder, signature.getProto(), signature.getName()); + } + + public MethodResolutionResult resolveMethodOnClassLegacy( + DexType holder, DexProto proto, DexString name) { + assert checkIfObsolete(); + return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + .resolveMethodOnClass(holder, proto, name); + } + + public MethodResolutionResult resolveMethodOnClassLegacy(DexClass clazz, DexMethod method) { + assert checkIfObsolete(); + return resolveMethodOnClassLegacy(clazz, method.getProto(), method.getName()); + } + + public MethodResolutionResult resolveMethodOnClassLegacy( + DexClass clazz, DexMethodSignature signature) { + assert checkIfObsolete(); + return resolveMethodOnClassLegacy(clazz, signature.getProto(), signature.getName()); + } + + public MethodResolutionResult resolveMethodOnClassLegacy( + DexClass clazz, DexProto proto, DexString name) { + assert checkIfObsolete(); + return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + .resolveMethodOnClass(clazz, proto, name); + } + + public MethodResolutionResult resolveMethodOnInterfaceHolderLegacy(DexMethod method) { + assert checkIfObsolete(); + return resolveMethodOnInterfaceLegacy(method.getHolderType(), method); + } + + public MethodResolutionResult resolveMethodOnInterfaceLegacy(DexType holder, DexMethod method) { + assert checkIfObsolete(); + return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + .resolveMethodOnInterface(holder, method.getProto(), method.getName()); + } + + public MethodResolutionResult resolveMethodOnInterfaceLegacy(DexClass clazz, DexMethod method) { + assert checkIfObsolete(); + return resolveMethodOnInterfaceLegacy(clazz, method.getProto(), method.getName()); + } + + public MethodResolutionResult resolveMethodOnInterfaceLegacy( + DexClass clazz, DexMethodSignature methodSignature) { + assert checkIfObsolete(); + return resolveMethodOnInterfaceLegacy( + clazz, methodSignature.getProto(), methodSignature.getName()); + } + + public MethodResolutionResult resolveMethodOnInterfaceLegacy( + DexClass clazz, DexProto proto, DexString name) { + assert checkIfObsolete(); + return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + .resolveMethodOnInterface(clazz, proto, name); + } + + /** + * This method will query the definition of the holder to decide on which resolution to use. + * + * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the + * kind of a method reference. + */ + public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) { + assert checkIfObsolete(); + return MethodResolution.create( + this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) + .unsafeResolveMethodDueToDexFormat(method); + } + public MethodResolutionResult resolveMethod(DexMethod invokedMethod, boolean isInterface) { assert checkIfObsolete(); return resolveMethodOn(invokedMethod.getHolderType(), invokedMethod, isInterface); } - public MethodResolutionResult resolveMethodOn(DexClass clazz, DexMethod method) { - assert checkIfObsolete(); - return clazz.isInterface() - ? resolveMethodOnInterface(clazz, method) - : resolveMethodOnClass(clazz, method); - } - - public MethodResolutionResult resolveMethodOn( - DexClass clazz, DexMethodSignature methodSignature) { - assert checkIfObsolete(); - return clazz.isInterface() - ? resolveMethodOnInterface(clazz, methodSignature) - : resolveMethodOnClass(clazz, methodSignature); - } - public MethodResolutionResult resolveMethodOn( DexType holder, DexMethod method, boolean isInterface) { assert checkIfObsolete(); @@ -698,7 +795,8 @@ public MethodResolutionResult resolveMethodOnClass( DexType holder, DexProto proto, DexString name) { assert checkIfObsolete(); - return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + return MethodResolution.create( + this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) .resolveMethodOnClass(holder, proto, name); } @@ -715,7 +813,8 @@ public MethodResolutionResult resolveMethodOnClass( DexClass clazz, DexProto proto, DexString name) { assert checkIfObsolete(); - return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + return MethodResolution.create( + this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) .resolveMethodOnClass(clazz, proto, name); } @@ -726,7 +825,8 @@ public MethodResolutionResult resolveMethodOnInterface(DexType holder, DexMethod method) { assert checkIfObsolete(); - return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + return MethodResolution.create( + this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) .resolveMethodOnInterface(holder, method.getProto(), method.getName()); } @@ -744,7 +844,8 @@ public MethodResolutionResult resolveMethodOnInterface( DexClass clazz, DexProto proto, DexString name) { assert checkIfObsolete(); - return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) + return MethodResolution.create( + this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) .resolveMethodOnInterface(clazz, proto, name); }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java index 4974a5d..8797a80 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCode.java +++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -27,7 +27,6 @@ import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata; import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription; import com.android.tools.r8.ir.code.IRCode; -import com.android.tools.r8.ir.code.MemberType; import com.android.tools.r8.ir.code.NumberGenerator; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.code.Position.SyntheticPosition; @@ -62,7 +61,6 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; -import java.util.function.BiPredicate; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -957,12 +955,7 @@ } CfFrameVerificationHelper builder = new CfFrameVerificationHelper( - previousMethodSignature.getHolderType(), - stateMap, - tryCatchRanges, - isAssignablePredicate(appView), - appView.dexItemFactory(), - maxStack); + appView, previousMethodSignature.getHolderType(), stateMap, tryCatchRanges, maxStack); for (CfTryCatch tryCatchRange : tryCatchRanges) { try { builder.checkTryCatchRange(tryCatchRange); @@ -1080,47 +1073,4 @@ } return initialLocals; } - - private BiPredicate<DexType, DexType> isAssignablePredicate(AppView<?> appView) { - return (source, target) -> isAssignable(source, target, appView); - } - - // Rules found at https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2 - private boolean isAssignable(DexType source, DexType target, AppView<?> appView) { - DexItemFactory factory = appView.dexItemFactory(); - source = byteCharShortOrBooleanToInt(source, factory); - target = byteCharShortOrBooleanToInt(target, factory); - if (source == target) { - return true; - } - if (source.isPrimitiveType() || target.isPrimitiveType()) { - return false; - } - // Both are now references - everything is assignable to object. - if (target == factory.objectType) { - return true; - } - // isAssignable(null, class(_, _)). - // isAssignable(null, arrayOf(_)). - if (source == DexItemFactory.nullValueType) { - return true; - } - if (target.isArrayType() != target.isArrayType()) { - return false; - } - if (target.isArrayType()) { - return isAssignable( - target.toArrayElementType(factory), target.toArrayElementType(factory), appView); - } - // TODO(b/166570659): Do a sub-type check that allows for missing classes in hierarchy. - return MemberType.fromDexType(source) == MemberType.fromDexType(target); - } - - private DexType byteCharShortOrBooleanToInt(DexType type, DexItemFactory factory) { - // byte, char, short and boolean has verification type int. - if (type.isByteType() || type.isCharType() || type.isShortType() || type.isBooleanType()) { - return factory.intType; - } - return type; - } }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java index 0927de2..a680b25 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java +++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -52,6 +52,11 @@ false); } + public static MethodResolution create( + Function<DexType, ClassResolutionResult> definitionFor, DexItemFactory factory) { + return new MethodResolution(definitionFor, factory, true); + } + private ClassResolutionResult definitionFor(DexType type) { return definitionFor.apply(type); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java index 6c8703b..6abb160 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -60,7 +60,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnClass(group.getSuperType(), template) + .resolveMethodOnClassLegacy(group.getSuperType(), template) .asSingleResolution(); if (resolutionResult == null || resolutionResult.getResolvedMethod().isAbstract()) { // If there is no super method or the method is abstract it should not be called.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java index fa9bbde..9856e0b 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
@@ -487,7 +487,7 @@ DexMethod rewrittenMethod = appView.graphLens().lookupInvokeDirect(method, getContext()).getReference(); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnClassHolder(rewrittenMethod); + appView.appInfo().resolveMethodOnClassHolderLegacy(rewrittenMethod); if (resolutionResult.isSingleResolution() && resolutionResult.getResolvedHolder().isProgramClass()) { enqueueMethod(resolutionResult.getResolvedProgramMethod()); @@ -499,7 +499,10 @@ DexMethod rewrittenMethod = appView.graphLens().lookupInvokeInterface(method, getContext()).getReference(); DexClassAndMethod resolvedMethod = - appView.appInfo().resolveMethodOnInterfaceHolder(rewrittenMethod).getResolutionPair(); + appView + .appInfo() + .resolveMethodOnInterfaceHolderLegacy(rewrittenMethod) + .getResolutionPair(); if (resolvedMethod != null) { fail(); } @@ -512,7 +515,7 @@ ProgramMethod resolvedMethod = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(rewrittenMethod) + .unsafeResolveMethodDueToDexFormatLegacy(rewrittenMethod) .getResolvedProgramMethod(); if (resolvedMethod != null) { triggerClassInitializerIfNotAlreadyTriggeredInContext(resolvedMethod.getHolder()); @@ -537,7 +540,7 @@ DexMethod rewrittenMethod = appView.graphLens().lookupInvokeVirtual(method, getContext()).getReference(); DexClassAndMethod resolvedMethod = - appView.appInfo().resolveMethodOnClassHolder(rewrittenMethod).getResolutionPair(); + appView.appInfo().resolveMethodOnClassHolderLegacy(rewrittenMethod).getResolutionPair(); if (resolvedMethod != null) { if (!resolvedMethod.getHolder().isEffectivelyFinal(appView)) { fail();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java index fecb888..07b2fc7 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -108,7 +108,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnClass(superType, method.getReference()) + .resolveMethodOnClassLegacy(superType, method.getReference()) .asSingleResolution(); return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract(); } @@ -121,7 +121,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnInterface(interfaceType, method.getReference()) + .resolveMethodOnInterfaceLegacy(interfaceType, method.getReference()) .asSingleResolution(); return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract(); });
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java index ca52f7c..2a3602c 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
@@ -171,7 +171,7 @@ if (clazzReserved.contains(signature)) { DexMethod template = signature.withHolder(clazz, appView.dexItemFactory()); SingleResolutionResult<?> result = - appView.appInfo().resolveMethodOnClass(clazz, template).asSingleResolution(); + appView.appInfo().resolveMethodOnClassLegacy(clazz, template).asSingleResolution(); if (result == null || result.getResolvedHolder().isInterface()) { category = MethodCategory.KEEP_ABSENT; }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java index 95f0385..bbf486f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java
@@ -216,7 +216,7 @@ ProgramMethod target = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(rewrittenMethod) + .unsafeResolveMethodDueToDexFormatLegacy(rewrittenMethod) .getResolvedProgramMethod(); if (target != null) { recordDispatchTarget(target);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java index 670f74a..9a7ddd8 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -333,7 +333,7 @@ } DexMethod method = instruction.getInvokedMethod(); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterface(method.holder, method); + appView.appInfo().resolveMethodOnInterfaceLegacy(method.holder, method); if (!resolutionResult.isSingleResolution()) { return false; } @@ -394,7 +394,7 @@ return false; } MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOn(superType, method, instruction.isInterface); + appView.appInfo().resolveMethodOnLegacy(superType, method, instruction.isInterface); if (!resolutionResult.isSingleResolution()) { return false; } @@ -431,7 +431,7 @@ } DexMethod method = instruction.getInvokedMethod(); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnClass(method.holder, method); + appView.appInfo().resolveMethodOnClassLegacy(method.holder, method); if (!resolutionResult.isSingleResolution()) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/ControlFlowGraph.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/ControlFlowGraph.java index 5710f6e..a6150bc 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/ControlFlowGraph.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/ControlFlowGraph.java
@@ -5,38 +5,155 @@ package com.android.tools.r8.ir.analysis.framework.intraprocedural; import com.android.tools.r8.utils.TraversalContinuation; -import com.google.common.collect.Iterables; -import java.util.Collection; +import com.android.tools.r8.utils.TraversalUtils; import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; public interface ControlFlowGraph<Block, Instruction> { - Collection<Block> getPredecessors(Block block); - - Collection<Block> getSuccessors(Block block); - default boolean hasUniquePredecessor(Block block) { - return getPredecessors(block).size() == 1; - } - - default Block getUniquePredecessor(Block block) { - assert hasUniquePredecessor(block); - return Iterables.getOnlyElement(getPredecessors(block)); + return TraversalUtils.isSingleton(counter -> traversePredecessors(block, counter)); } default boolean hasUniqueSuccessor(Block block) { - return getSuccessors(block).size() == 1; + return TraversalUtils.isSingleton(counter -> traverseSuccessors(block, counter)); } default boolean hasUniqueSuccessorWithUniquePredecessor(Block block) { - return hasUniqueSuccessor(block) && getPredecessors(getUniqueSuccessor(block)).size() == 1; + return hasUniqueSuccessor(block) && hasUniquePredecessor(getUniqueSuccessor(block)); } default Block getUniqueSuccessor(Block block) { assert hasUniqueSuccessor(block); - return Iterables.getOnlyElement(getSuccessors(block)); + return TraversalUtils.getFirst(collector -> traverseSuccessors(block, collector)); } + // Block traversal. + + default <BT, CT> TraversalContinuation<BT, CT> traversePredecessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traversePredecessors(block, (predecessor, ignore) -> fn.apply(predecessor), null); + } + + default <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traverseNormalPredecessors(block, (predecessor, ignore) -> fn.apply(predecessor), null); + } + + default <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traverseExceptionalPredecessors( + block, (predecessor, ignore) -> fn.apply(predecessor), null); + } + + default <BT, CT> TraversalContinuation<BT, CT> traverseSuccessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traverseSuccessors(block, (successor, ignore) -> fn.apply(successor), null); + } + + default <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traverseNormalSuccessors(block, (successor, ignore) -> fn.apply(successor), null); + } + + default <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( + Block block, Function<? super Block, TraversalContinuation<BT, CT>> fn) { + return traverseExceptionalSuccessors(block, (successor, ignore) -> fn.apply(successor), null); + } + + // Block traversal with result. + + default <BT, CT> TraversalContinuation<BT, CT> traversePredecessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return traverseNormalPredecessors(block, fn, initialValue) + .ifContinueThen( + continuation -> + traverseExceptionalPredecessors(block, fn, continuation.getValueOrDefault(null))); + } + + <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue); + + <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue); + + default <BT, CT> TraversalContinuation<BT, CT> traverseSuccessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return traverseNormalSuccessors(block, fn, initialValue) + .ifContinueThen( + continuation -> + traverseExceptionalSuccessors(block, fn, continuation.getValueOrDefault(null))); + } + + <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue); + + <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( + Block block, + BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue); + + // Block iteration. + + default void forEachPredecessor(Block block, Consumer<Block> consumer) { + forEachNormalPredecessor(block, consumer); + forEachExceptionalPredecessor(block, consumer); + } + + default void forEachNormalPredecessor(Block block, Consumer<Block> consumer) { + traverseNormalPredecessors( + block, + predecessor -> { + consumer.accept(predecessor); + return TraversalContinuation.doContinue(); + }); + } + + default void forEachExceptionalPredecessor(Block block, Consumer<Block> consumer) { + traverseExceptionalPredecessors( + block, + exceptionalPredecessor -> { + consumer.accept(exceptionalPredecessor); + return TraversalContinuation.doContinue(); + }); + } + + default void forEachSuccessor(Block block, Consumer<Block> consumer) { + forEachNormalSuccessor(block, consumer); + forEachExceptionalSuccessor(block, consumer); + } + + default void forEachNormalSuccessor(Block block, Consumer<Block> consumer) { + traverseNormalSuccessors( + block, + successor -> { + consumer.accept(successor); + return TraversalContinuation.doContinue(); + }); + } + + default void forEachExceptionalSuccessor(Block block, Consumer<Block> consumer) { + traverseExceptionalSuccessors( + block, + exceptionalSuccessor -> { + consumer.accept(exceptionalSuccessor); + return TraversalContinuation.doContinue(); + }); + } + + // Instruction traversal. + <BT, CT> TraversalContinuation<BT, CT> traverseInstructions( Block block, BiFunction<Instruction, CT, TraversalContinuation<BT, CT>> fn, CT initialValue); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IRControlFlowGraph.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IRControlFlowGraph.java new file mode 100644 index 0000000..aae27f7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IRControlFlowGraph.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.ir.analysis.framework.intraprocedural; + +import com.android.tools.r8.ir.code.BasicBlock; +import com.android.tools.r8.ir.code.Instruction; + +public interface IRControlFlowGraph extends ControlFlowGraph<BasicBlock, Instruction> { + + @Override + default boolean hasUniquePredecessor(BasicBlock block) { + return block.hasUniquePredecessor(); + } + + @Override + default boolean hasUniqueSuccessor(BasicBlock block) { + return block.hasUniqueSuccessor(); + } + + @Override + default boolean hasUniqueSuccessorWithUniquePredecessor(BasicBlock block) { + return block.hasUniqueSuccessorWithUniquePredecessor(); + } + + @Override + default BasicBlock getUniqueSuccessor(BasicBlock block) { + return block.getUniqueSuccessor(); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java index 969197f..f904da3 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.TraversalContinuation; +import com.android.tools.r8.utils.TraversalUtils; import com.android.tools.r8.utils.WorkList; import java.util.IdentityHashMap; import java.util.Map; @@ -100,7 +101,7 @@ // Update the block exit state, and re-enqueue all successor blocks if the abstract state // changed. if (setBlockExitState(end, state)) { - worklist.addAllIgnoringSeenSet(cfg.getSuccessors(end)); + cfg.forEachSuccessor(end, worklist::addIgnoringSeenSet); } // Add the computed exit state to the entry state of each successor that satisfies the @@ -114,14 +115,17 @@ if (shouldCacheBlockEntryStateFor(block)) { return blockEntryStatesCache.getOrDefault(block, bottom); } - StateType result = bottom; - for (Block predecessor : cfg.getPredecessors(block)) { - StateType edgeState = - transfer.computeBlockEntryState( - block, predecessor, blockExitStates.getOrDefault(predecessor, bottom)); - result = result.join(edgeState); - } - return result; + TraversalContinuation<?, StateType> traversalContinuation = + cfg.traversePredecessors( + block, + (predecessor, entryState) -> { + StateType edgeState = + transfer.computeBlockEntryState( + block, predecessor, blockExitStates.getOrDefault(predecessor, bottom)); + return TraversalContinuation.doContinue(entryState.join(edgeState)); + }, + bottom); + return traversalContinuation.asContinue().getValue(); } boolean setBlockExitState(Block block, StateType state) { @@ -132,16 +136,18 @@ } void updateBlockEntryStateCacheForSuccessors(Block block, StateType state) { - for (Block successor : cfg.getSuccessors(block)) { - if (shouldCacheBlockEntryStateFor(successor)) { - StateType edgeState = transfer.computeBlockEntryState(successor, block, state); - StateType previous = blockEntryStatesCache.getOrDefault(successor, bottom); - blockEntryStatesCache.put(successor, previous.join(edgeState)); - } - } + cfg.forEachSuccessor( + block, + successor -> { + if (shouldCacheBlockEntryStateFor(successor)) { + StateType edgeState = transfer.computeBlockEntryState(successor, block, state); + StateType previous = blockEntryStatesCache.getOrDefault(successor, bottom); + blockEntryStatesCache.put(successor, previous.join(edgeState)); + } + }); } boolean shouldCacheBlockEntryStateFor(Block block) { - return cfg.getPredecessors(block).size() > 2; + return TraversalUtils.isSizeGreaterThan(counter -> cfg.traversePredecessors(block, counter), 2); } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java index 13a9c42..789a2cb 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
@@ -17,4 +17,9 @@ AbstractTransferFunction<BasicBlock, Instruction, StateType> transfer) { super(bottom, code, transfer); } + + @Override + boolean shouldCacheBlockEntryStateFor(BasicBlock block) { + return block.getPredecessors().size() > 2; + } }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java new file mode 100644 index 0000000..b86915c --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java
@@ -0,0 +1,97 @@ +// 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.analysis.framework.intraprocedural.cf; + +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.utils.SetUtils; +import java.util.ArrayList; +import java.util.List; + +/** A basic block for {@link com.android.tools.r8.graph.CfCode}. */ +public class CfBlock { + + // The CfCode instruction index of the block's first instruction. + int firstInstructionIndex = -1; + + // The CfCode instruction index of the block's last instruction. + int lastInstructionIndex = -1; + + // The predecessors of the block. These are stored explicitly (unlike the successors) since they + // cannot efficiently be computed from the block. + final List<CfBlock> predecessors = new ArrayList<>(); + + // The exceptional predecessors of the block. + final List<CfBlock> exceptionalPredecessors = new ArrayList<>(); + + // The exceptional successors of the block (i.e., the catch handlers of the block). + final List<CfBlock> exceptionalSuccessors = new ArrayList<>(); + + public CfInstruction getFallthroughInstruction(CfCode code) { + int fallthroughInstructionIndex = getLastInstructionIndex() + 1; + return fallthroughInstructionIndex < code.getInstructions().size() + ? code.getInstructions().get(fallthroughInstructionIndex) + : null; + } + + public int getFirstInstructionIndex() { + return firstInstructionIndex; + } + + public CfInstruction getLastInstruction(CfCode code) { + return code.getInstructions().get(lastInstructionIndex); + } + + public int getLastInstructionIndex() { + return lastInstructionIndex; + } + + public List<CfBlock> getPredecessors() { + return predecessors; + } + + // TODO(b/214496607): This currently only encodes the graph, but we likely need to include the + // guard types here. + public List<CfBlock> getExceptionalPredecessors() { + return exceptionalPredecessors; + } + + // TODO(b/214496607): This currently only encodes the graph, but we likely need to include the + // guard types here. + public List<CfBlock> getExceptionalSuccessors() { + return exceptionalSuccessors; + } + + // A mutable interface for block construction. + static class MutableCfBlock extends CfBlock { + + void addPredecessor(CfBlock block) { + predecessors.add(block); + } + + void addExceptionalPredecessor(CfBlock block) { + exceptionalPredecessors.add(block); + } + + void addExceptionalSuccessor(CfBlock block) { + exceptionalSuccessors.add(block); + } + + void setFirstInstructionIndex(int firstInstructionIndex) { + this.firstInstructionIndex = firstInstructionIndex; + } + + void setLastInstructionIndex(int lastInstructionIndex) { + this.lastInstructionIndex = lastInstructionIndex; + } + + boolean validate() { + assert 0 <= firstInstructionIndex; + assert firstInstructionIndex <= lastInstructionIndex; + assert SetUtils.newIdentityHashSet(predecessors).size() == predecessors.size(); + return true; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java new file mode 100644 index 0000000..4cf9e5e --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java
@@ -0,0 +1,312 @@ +// 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.analysis.framework.intraprocedural.cf; + +import static com.android.tools.r8.utils.MapUtils.ignoreKey; + +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfLabel; +import com.android.tools.r8.cf.code.CfTryCatch; +import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.ControlFlowGraph; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfBlock.MutableCfBlock; +import com.android.tools.r8.utils.IterableUtils; +import com.android.tools.r8.utils.TraversalContinuation; +import com.android.tools.r8.utils.TraversalUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; + +/** + * The following provides a control flow graph for a piece of {@link CfCode}. + * + * <p>In the {@link CfControlFlowGraph}, each instruction that is the target of a jump (including + * fallthrough targets following jumps) starts a new basic block. The first instruction in {@link + * CfCode} also starts a new block. + * + * <p>Each block is identified by the first instruction of the block. + */ +public class CfControlFlowGraph implements ControlFlowGraph<CfBlock, CfInstruction> { + + // Mapping from block entry instructions to cf blocks. + private final Map<CfInstruction, ? extends CfBlock> blocks; + private final CfCode code; + + private CfControlFlowGraph(Map<CfInstruction, ? extends CfBlock> blocks, CfCode code) { + this.blocks = blocks; + this.code = code; + } + + private static Builder builder(CfCode code) { + return new Builder(code); + } + + public static CfControlFlowGraph create(CfCode code) { + return builder(code).build(); + } + + private CfBlock getBlock(CfInstruction blockEntry) { + assert blocks.containsKey(blockEntry); + return blocks.get(blockEntry); + } + + public CfBlock getEntryBlock() { + return getBlock(code.getInstructions().get(0)); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( + CfBlock block, + BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return TraversalUtils.traverseIterable(block.getPredecessors(), fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( + CfBlock block, + BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return TraversalUtils.traverseIterable(block.getExceptionalPredecessors(), fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( + CfBlock block, + BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + CfInstruction blockExit = block.getLastInstruction(code); + CfInstruction fallthroughInstruction = block.getFallthroughInstruction(code); + return blockExit.traverseNormalTargets( + (target, value) -> fn.apply(getBlock(target), value), fallthroughInstruction, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( + CfBlock block, + BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return TraversalUtils.traverseIterable(block.getExceptionalSuccessors(), fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseInstructions( + CfBlock block, + BiFunction<CfInstruction, CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (int instructionIndex = block.getFirstInstructionIndex(); + instructionIndex <= block.getLastInstructionIndex(); + instructionIndex++) { + CfInstruction instruction = code.getInstructions().get(instructionIndex); + traversalContinuation = fn.apply(instruction, traversalContinuation.asContinue().getValue()); + if (traversalContinuation.shouldBreak()) { + break; + } + } + return traversalContinuation; + } + + private static class Builder { + + // Mapping from block entry instructions to the block that starts at each such instruction. + private final Map<CfInstruction, MutableCfBlock> blocks = new IdentityHashMap<>(); + + private final CfCode code; + + Builder(CfCode code) { + this.code = code; + } + + CfControlFlowGraph build() { + // Perform an initial pass over the CfCode to identify all instructions that start a new + // block. + createBlocks(); + + // Perform a second pass over the CfCode to finalize the identified blocks. This includes + // setting the instruction index of the last instruction of each block, which relies on having + // identified all block entries up front. + processBlocks(); + + assert blocks.values().stream().allMatch(MutableCfBlock::validate); + + return new CfControlFlowGraph(blocks, code); + } + + private void createBlocks() { + List<CfInstruction> instructions = code.getInstructions(); + + // The first instruction starts the first block. + createBlockIfAbsent(instructions.get(0)); + + // Create a block for each instruction that is targeted by a jump or fallthrough. + for (int instructionIndex = 0; instructionIndex < instructions.size(); instructionIndex++) { + CfInstruction instruction = instructions.get(instructionIndex); + if (instruction.isJump()) { + int fallthroughInstructionIndex = instructionIndex + 1; + CfInstruction fallthroughInstruction = + fallthroughInstructionIndex < instructions.size() + ? instructions.get(fallthroughInstructionIndex) + : null; + instruction.forEachNormalTarget(this::createBlockIfAbsent, fallthroughInstruction); + } + } + + for (CfTryCatch tryCatch : code.getTryCatchRanges()) { + // Create a new block at the beginning and end of each try range. This is needed to ensure + // that each block has the same set of catch handlers. + createBlockIfAbsent(tryCatch.getStart()); + createBlockIfAbsent(tryCatch.getEnd()); + + // Create a new block for the beginning of each catch handler. + tryCatch.forEachTarget(this::createBlockIfAbsent); + } + } + + private void processBlocks() { + // A collection of active catch handlers. The catch handlers are stored in a map where the key + // is the label at which the catch handlers end. + Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers = new IdentityHashMap<>(); + + // A collection of inactive catch handlers. The catch handlers are stored in a map where the + // key is the label at which the catch handlers start. + Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers = new IdentityHashMap<>(); + + // Initialize all catch handlers to be inactive. + for (CfTryCatch tryCatch : code.getTryCatchRanges()) { + inactiveUntilCatchHandlers + .computeIfAbsent(tryCatch.getStart(), ignoreKey(ArrayList::new)) + .add(tryCatch); + } + + // Process each instruction. + List<CfInstruction> instructions = code.getInstructions(); + for (int instructionIndex = 0; instructionIndex < instructions.size(); instructionIndex++) { + CfInstruction instruction = instructions.get(instructionIndex); + MutableCfBlock block = getBlockOrNull(instruction); + if (block != null) { + instructionIndex = + processBlock( + instruction, + instructionIndex, + block, + activeUntilCatchHandlers, + inactiveUntilCatchHandlers); + } + } + + assert activeUntilCatchHandlers.isEmpty(); + assert inactiveUntilCatchHandlers.isEmpty(); + } + + private int processBlock( + CfInstruction instruction, + int instructionIndex, + MutableCfBlock block, + Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers, + Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers) { + // Record the index of the first instruction of the block. + block.setFirstInstructionIndex(instructionIndex); + + if (instruction.isLabel()) { + updateCatchHandlers( + instruction.asLabel(), activeUntilCatchHandlers, inactiveUntilCatchHandlers); + } + + // Visit each instruction belonging to the current block. + Set<CfLabel> exceptionalSuccessors = new LinkedHashSet<>(); + do { + assert !instruction.isLabel() + || verifyCatchHandlersUnchanged( + instruction.asLabel(), activeUntilCatchHandlers, inactiveUntilCatchHandlers); + if (instruction.canThrow()) { + for (CfTryCatch tryCatch : IterableUtils.flatten(activeUntilCatchHandlers.values())) { + exceptionalSuccessors.addAll(tryCatch.getTargets()); + } + } + if (isBlockExit(instructionIndex)) { + break; + } + instruction = code.getInstructions().get(++instructionIndex); + } while (true); + + // Record the index of the last instruction of the block. + block.setLastInstructionIndex(instructionIndex); + + // Add the current block as a predecessor of the successor blocks. + CfInstruction fallthroughInstruction = block.getFallthroughInstruction(code); + instruction.forEachNormalTarget( + target -> getBlock(target).addPredecessor(block), fallthroughInstruction); + + // Add the current block as an exceptional predecessor of the exceptional successor blocks. + exceptionalSuccessors.forEach( + exceptionalSuccessor -> { + MutableCfBlock exceptionalSuccessorBlock = getBlock(exceptionalSuccessor); + block.addExceptionalSuccessor(exceptionalSuccessorBlock); + exceptionalSuccessorBlock.addExceptionalPredecessor(block); + }); + + return instructionIndex; + } + + private boolean isBlockEntry(CfInstruction instruction) { + return blocks.containsKey(instruction); + } + + private boolean isBlockExit(int instructionIndex) { + int lastInstructionIndex = code.getInstructions().size() - 1; + if (instructionIndex == lastInstructionIndex) { + return true; + } + CfInstruction nextInstruction = code.getInstructions().get(instructionIndex + 1); + return isBlockEntry(nextInstruction); + } + + private void updateCatchHandlers( + CfLabel instruction, + Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers, + Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers) { + // Remove active catch handlers that have expired at the current instruction. + activeUntilCatchHandlers.remove(instruction); + + // Promote inactive catch handlers that is activated at the current instruction to active. + for (CfTryCatch tryCatch : + inactiveUntilCatchHandlers.getOrDefault(instruction, Collections.emptyList())) { + assert tryCatch.getEnd() != tryCatch.getStart(); + activeUntilCatchHandlers + .computeIfAbsent(tryCatch.getEnd(), ignoreKey(ArrayList::new)) + .add(tryCatch); + } + } + + private boolean verifyCatchHandlersUnchanged( + CfLabel instruction, + Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers, + Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers) { + assert !activeUntilCatchHandlers.containsKey(instruction); + assert !inactiveUntilCatchHandlers.containsKey(instruction); + return true; + } + + private void createBlockIfAbsent(CfInstruction blockEntry) { + blocks.computeIfAbsent(blockEntry, ignoreKey(MutableCfBlock::new)); + } + + private MutableCfBlock getBlock(CfInstruction blockEntry) { + assert blocks.containsKey(blockEntry); + return blocks.get(blockEntry); + } + + private MutableCfBlock getBlockOrNull(CfInstruction blockEntry) { + return blocks.get(blockEntry); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java new file mode 100644 index 0000000..b4ca696 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
@@ -0,0 +1,21 @@ +// 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.analysis.framework.intraprocedural.cf; + +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisBase; + +public class CfIntraproceduralDataflowAnalysis<StateType extends AbstractState<StateType>> + extends IntraProceduralDataflowAnalysisBase<CfBlock, CfInstruction, StateType> { + + public CfIntraproceduralDataflowAnalysis( + StateType bottom, + CfControlFlowGraph cfg, + AbstractTransferFunction<CfBlock, CfInstruction, StateType> transfer) { + super(bottom, cfg, transfer); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java index b664c13..0d1bf75 100644 --- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java +++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -25,6 +25,7 @@ import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.StringUtils.BraceType; +import com.android.tools.r8.utils.TraversalContinuation; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; @@ -48,6 +49,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.WeakHashMap; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -119,7 +121,7 @@ public enum ThrowingInfo { NO_THROW, - CAN_THROW; + CAN_THROW } public enum EdgeType { @@ -189,6 +191,73 @@ // Map of registers to current SSA value. Used during SSA numbering and cleared once filled. private Map<Integer, Value> currentDefinitions = new HashMap<>(); + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (BasicBlock predecessor : getPredecessors()) { + if (predecessor.hasCatchSuccessor(this)) { + continue; + } + traversalContinuation = + fn.apply(predecessor, traversalContinuation.asContinue().getValueOrDefault(null)); + if (traversalContinuation.isBreak()) { + break; + } + } + return traversalContinuation; + } + + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) { + traversalContinuation = + fn.apply(successors.get(i), traversalContinuation.asContinue().getValueOrDefault(null)); + if (traversalContinuation.isBreak()) { + break; + } + } + return traversalContinuation; + } + + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (BasicBlock predecessor : getPredecessors()) { + if (!predecessor.hasCatchSuccessor(this)) { + continue; + } + traversalContinuation = + fn.apply(predecessor, traversalContinuation.asContinue().getValueOrDefault(null)); + if (traversalContinuation.isBreak()) { + break; + } + } + return traversalContinuation; + } + + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + int numberOfExceptionalSuccessors = numberOfExceptionalSuccessors(); + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (int i = 0; i < numberOfExceptionalSuccessors; i++) { + traversalContinuation = + fn.apply(successors.get(i), traversalContinuation.asContinue().getValueOrDefault(null)); + if (traversalContinuation.isBreak()) { + break; + } + } + return traversalContinuation; + } + public void addControlFlowEdgesMayChangeListener(BasicBlockChangeListener listener) { if (onControlFlowEdgesMayChangeListeners == null) { // WeakSet to allow the listeners to be garbage collected.
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java index b8eb09d..552ebf3 100644 --- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java +++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -202,7 +202,7 @@ DexItemFactory dexItemFactory = appView.dexItemFactory(); DexEncodedMethod resolutionResult = appInfo - .resolveMethodOnClass(clazz, dexItemFactory.objectMembers.finalize) + .resolveMethodOnClassLegacy(clazz, dexItemFactory.objectMembers.finalize) .getSingleTarget(); return resolutionResult != null && resolutionResult.isProgramMethod(appView); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index 53fa359..bf8f7ad 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -16,7 +16,7 @@ import com.android.tools.r8.graph.classmerging.MergedClassesCollection; import com.android.tools.r8.ir.analysis.TypeChecker; import com.android.tools.r8.ir.analysis.VerifyTypesHelper; -import com.android.tools.r8.ir.analysis.framework.intraprocedural.ControlFlowGraph; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.IRControlFlowGraph; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.type.TypeElement; @@ -64,7 +64,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class IRCode implements ControlFlowGraph<BasicBlock, Instruction>, ValueFactory { +public class IRCode implements IRControlFlowGraph, ValueFactory { private static final int MAX_MARKING_COLOR = 0x40000000; @@ -1352,17 +1352,47 @@ return blocks; } - @Override public Collection<BasicBlock> getPredecessors(BasicBlock block) { return block.getPredecessors(); } - @Override public Collection<BasicBlock> getSuccessors(BasicBlock block) { return block.getSuccessors(); } @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( + BasicBlock block, + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return block.traverseNormalPredecessors(fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( + BasicBlock block, + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return block.traverseNormalSuccessors(fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( + BasicBlock block, + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return block.traverseExceptionalPredecessors(fn, initialValue); + } + + @Override + public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( + BasicBlock block, + BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + return block.traverseExceptionalSuccessors(fn, initialValue); + } + + @Override public <BT, CT> TraversalContinuation<BT, CT> traverseInstructions( BasicBlock block, BiFunction<Instruction, CT, TraversalContinuation<BT, CT>> fn,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java index 4e77680..ec23162 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -151,7 +151,7 @@ } } MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethod(method, getInterfaceBit()); + appView.appInfo().resolveMethodLegacy(method, getInterfaceBit()); LookupResult lookupResult; if (refinedReceiverUpperBound != null) { lookupResult =
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java index c13800c..668d515 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -200,7 +200,7 @@ SingleResolutionResult<?> resolutionResult = appViewWithClassHierarchy .appInfo() - .resolveMethod(getInvokedMethod(), getInterfaceBit()) + .resolveMethodLegacy(getInvokedMethod(), getInterfaceBit()) .asSingleResolution(); if (resolutionResult == null) { return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java index f39baa4..b405dbb 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -199,7 +199,7 @@ SingleResolutionResult<?> resolutionResult = appViewWithLiveness .appInfo() - .resolveMethod(getInvokedMethod(), isInterface) + .resolveMethodLegacy(getInvokedMethod(), isInterface) .asSingleResolution(); // Verify that the target method is present.
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java index baae8e5..85bae5c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java +++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -194,7 +194,7 @@ MethodResolutionResult finalizeResolutionResult = appViewWithClassHierarchy .appInfo() - .resolveMethodOnClass(clazz, dexItemFactory.objectMembers.finalize); + .resolveMethodOnClassLegacy(clazz, dexItemFactory.objectMembers.finalize); if (finalizeResolutionResult.isSingleResolution()) { DexMethod finalizeMethod = finalizeResolutionResult.getSingleTarget().getReference(); if (finalizeMethod != dexItemFactory.enumMembers.finalize
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 fc23097..fa7a539 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
@@ -94,12 +94,12 @@ import com.android.tools.r8.utils.ExceptionUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.NeverMergeGroup; +import com.android.tools.r8.utils.LazyBox; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.google.common.base.Suppliers; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collections; @@ -1175,7 +1175,7 @@ return timing; } - assertionsRewriter.run(method, code, timing); + assertionsRewriter.run(method, code, deadCodeRemover, timing); if (serviceLoaderRewriter != null) { assert appView.appInfo().hasLiveness(); @@ -1402,7 +1402,7 @@ methodProcessor, methodProcessingContext, inliner, - Suppliers.memoize( + new LazyBox<>( () -> inliner.createDefaultOracle( code.context(),
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java index edd4f14..3fc7ad0 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -78,7 +78,7 @@ if (type == Invoke.Type.INTERFACE || type == Invoke.Type.VIRTUAL) { // For virtual and interface calls add all potential targets that could be called. MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethod(method, type == Invoke.Type.INTERFACE); + appView.appInfo().resolveMethodLegacy(method, type == Invoke.Type.INTERFACE); DexClassAndMethod target = resolutionResult.getResolutionPair(); if (target != null) { processInvokeWithDynamicDispatch(type, target, context); @@ -115,7 +115,7 @@ target, method -> { MethodResolutionResult resolution = - appView.appInfo().resolveMethod(method, isInterface); + appView.appInfo().resolveMethodLegacy(method, isInterface); if (resolution.isVirtualTarget()) { LookupResult lookupResult = resolution.lookupVirtualDispatchTargets(context.getHolder(), appView.appInfo());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index 012c31c..ac5addd 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -373,7 +373,7 @@ assert descriptor.verifyTargetFoundInClass(accessedFrom.getHolderType()); if (implHandle.type.isInvokeStatic()) { MethodResolutionResult resolution = - appView.appInfoForDesugaring().resolveMethod(implMethod, implHandle.isInterface); + appView.appInfoForDesugaring().resolveMethodLegacy(implMethod, implHandle.isInterface); if (resolution.isFailedResolution()) { return new InvalidLambdaImplTarget( implMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java index bc7a544..d6b10b0 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -123,7 +123,7 @@ case INVOKE_INSTANCE: { DexEncodedMethod target = appInfo - .resolveMethodOn(getImplReceiverType(), method, implHandle.isInterface) + .resolveMethodOnLegacy(getImplReceiverType(), method, implHandle.isInterface) .getSingleTarget(); if (target == null) { target = appInfo.lookupDirectTarget(method, context); @@ -149,7 +149,9 @@ case INVOKE_INTERFACE: { DexEncodedMethod target = - appInfo.resolveMethodOnInterface(getImplReceiverType(), method).getSingleTarget(); + appInfo + .resolveMethodOnInterfaceLegacy(getImplReceiverType(), method) + .getSingleTarget(); assert target == null || isInstanceMethod(target); return target; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java index 8935159..e408232 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -120,7 +120,7 @@ MethodResolutionResult resolution = appView .appInfoForDesugaring() - .resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface); + .resolveMethodLegacy(bootstrapMethodReference, bootstrapMethodHandle.isInterface); if (resolution.isSingleResolution() && resolution.asSingleResolution().getResolvedMethod().isStatic()) { SingleResolutionResult<?> result = resolution.asSingleResolution();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java new file mode 100644 index 0000000..66a980a --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/ApiLevelRange.java
@@ -0,0 +1,77 @@ +// 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; + +import com.android.tools.r8.utils.AndroidApiLevel; +import java.util.Objects; + +public class ApiLevelRange { + + private final AndroidApiLevel apiLevelBelowOrEqual; + private final AndroidApiLevel apiLevelGreaterOrEqual; + + public ApiLevelRange(int apiLevelBelowOrEqual) { + this(AndroidApiLevel.getAndroidApiLevel(apiLevelBelowOrEqual), null); + } + + public ApiLevelRange(int apiLevelBelowOrEqual, int apiLevelGreaterOrEqual) { + this( + AndroidApiLevel.getAndroidApiLevel(apiLevelBelowOrEqual), + AndroidApiLevel.getAndroidApiLevel(apiLevelGreaterOrEqual)); + } + + public ApiLevelRange( + AndroidApiLevel apiLevelBelowOrEqual, AndroidApiLevel apiLevelGreaterOrEqual) { + this.apiLevelBelowOrEqual = apiLevelBelowOrEqual; + this.apiLevelGreaterOrEqual = apiLevelGreaterOrEqual; + } + + public int getApiLevelBelowOrEqualAsInt() { + return apiLevelBelowOrEqual.getLevel(); + } + + public int getApiLevelGreaterOrEqualAsInt() { + return apiLevelGreaterOrEqual.getLevel(); + } + + public boolean hasApiLevelGreaterOrEqual() { + return apiLevelGreaterOrEqual != null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ApiLevelRange)) { + return false; + } + ApiLevelRange that = (ApiLevelRange) o; + return apiLevelBelowOrEqual.equals(that.apiLevelBelowOrEqual) + && apiLevelGreaterOrEqual.equals(that.apiLevelGreaterOrEqual); + } + + @Override + public int hashCode() { + return Objects.hash(apiLevelBelowOrEqual, apiLevelGreaterOrEqual); + } + + public int deterministicOrder(ApiLevelRange other) { + int compare = apiLevelBelowOrEqual.compareTo(other.apiLevelBelowOrEqual); + if (compare != 0) { + return compare; + } + if (apiLevelGreaterOrEqual == null) { + if (other.apiLevelGreaterOrEqual == null) { + return 0; + } + return 1; + } + if (other.apiLevelGreaterOrEqual == null) { + return -1; + } + return apiLevelGreaterOrEqual.compareTo(other.apiLevelGreaterOrEqual); + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java index f041fe3..55abcef 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -139,7 +139,7 @@ ? appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context) : appView .appInfoForDesugaring() - .resolveMethod(invokedMethod, invoke.isInterface()) + .resolveMethodLegacy(invokedMethod, invoke.isInterface()) .getResolutionPair(); }
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 79f1ff2..47f48d4 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
@@ -287,7 +287,7 @@ + invokedMethod.holder + "#" + invokedMethod.name - + " may not work correctly at runtime (Cannot convert type " + + " may not work correctly at runtime (No conversion registered for type " + desugaredType + ").", origin, @@ -520,12 +520,13 @@ .setInterfaces(interfaces) .setSuperType(superType) .setInstanceFields(Collections.singletonList(wrapperField)) - .addMethod(methodBuilder -> buildWrapperConstructor(wrapperField, methodBuilder)); + .addMethod( + methodBuilder -> buildWrapperConstructor(wrapperField, methodBuilder, superType)); return wrapperField; } private void buildWrapperConstructor( - DexEncodedField wrappedValueField, SyntheticMethodBuilder methodBuilder) { + DexEncodedField wrappedValueField, SyntheticMethodBuilder methodBuilder, DexType superType) { methodBuilder .setName(factory.constructorMethodName) .setProto(factory.createProto(factory.voidType, wrappedValueField.getType())) @@ -536,7 +537,8 @@ .disableAndroidApiLevelCheck() .setCode( codeSynthesizor -> - new APIConverterConstructorCfCodeProvider(appView, wrappedValueField.getReference()) + new APIConverterConstructorCfCodeProvider( + appView, wrappedValueField.getReference(), superType) .generateCfCode()); } @@ -650,7 +652,7 @@ @Override public String uniqueIdentifier() { - return "$wrapper$"; + return "$wrapper"; } // Program wrappers are harder to deal with than classpath wrapper because generating a method's
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java index 5936d8c..027a2ae 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -47,10 +47,12 @@ static final String PROGRAM_FLAGS_KEY = "program_flags"; static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal"; + static final String API_LEVEL_GREATER_OR_EQUAL_KEY = "api_level_greater_or_equal"; static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion"; static final String WRAPPER_CONVERSION_EXCLUDING_KEY = "wrapper_conversion_excluding"; static final String CUSTOM_CONVERSION_KEY = "custom_conversion"; static final String REWRITE_PREFIX_KEY = "rewrite_prefix"; + static final String DONT_REWRITE_PREFIX_KEY = "dont_rewrite_prefix"; static final String MAINTAIN_PREFIX_KEY = "maintain_prefix"; static final String RETARGET_STATIC_FIELD_KEY = "retarget_static_field"; static final String RETARGET_METHOD_KEY = "retarget_method"; @@ -231,7 +233,13 @@ JsonObject flag = jsonFlagSet.getAsJsonObject(); int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt(); if (minAPILevel <= api_level_below_or_equal) { - parseFlags(flag, builder); + if (flag.has(API_LEVEL_GREATER_OR_EQUAL_KEY)) { + if (minAPILevel >= flag.get(API_LEVEL_GREATER_OR_EQUAL_KEY).getAsInt()) { + parseFlags(flag, builder); + } + } else { + parseFlags(flag, builder); + } } } } @@ -248,6 +256,12 @@ builder.putMaintainPrefix(maintainPrefix.getAsString()); } } + if (jsonFlagSet.has(DONT_REWRITE_PREFIX_KEY)) { + for (JsonElement dontRewritePrefix : + jsonFlagSet.get(DONT_REWRITE_PREFIX_KEY).getAsJsonArray()) { + builder.putDontRewritePrefix(dontRewritePrefix.getAsString()); + } + } if (jsonFlagSet.has(REWRITE_DERIVED_PREFIX_KEY)) { for (Map.Entry<String, JsonElement> prefixToMatch : jsonFlagSet.get(REWRITE_DERIVED_PREFIX_KEY).getAsJsonObject().entrySet()) {
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 50e98e1..fc5bcca 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
@@ -26,6 +26,7 @@ public class HumanRewritingFlags { private final Map<String, String> rewritePrefix; + private final Set<String> dontRewritePrefix; private final Set<String> maintainPrefix; private final Map<String, Map<String, String>> rewriteDerivedPrefix; private final Map<DexType, DexType> emulatedInterfaces; @@ -42,6 +43,7 @@ HumanRewritingFlags( Map<String, String> rewritePrefix, + Set<String> dontRewritePrefix, Set<String> maintainPrefix, Map<String, Map<String, String>> rewriteDerivedPrefix, Map<DexType, DexType> emulateLibraryInterface, @@ -56,6 +58,7 @@ Map<DexMethod, MethodAccessFlags> amendLibraryMethod, Map<DexField, FieldAccessFlags> amendLibraryField) { this.rewritePrefix = rewritePrefix; + this.dontRewritePrefix = dontRewritePrefix; this.maintainPrefix = maintainPrefix; this.rewriteDerivedPrefix = rewriteDerivedPrefix; this.emulatedInterfaces = emulateLibraryInterface; @@ -75,6 +78,7 @@ return new HumanRewritingFlags( ImmutableMap.of(), ImmutableSet.of(), + ImmutableSet.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), @@ -98,6 +102,7 @@ reporter, origin, rewritePrefix, + dontRewritePrefix, maintainPrefix, rewriteDerivedPrefix, emulatedInterfaces, @@ -117,6 +122,10 @@ return rewritePrefix; } + public Set<String> getDontRewritePrefix() { + return dontRewritePrefix; + } + public Set<String> getMaintainPrefix() { return maintainPrefix; } @@ -185,6 +194,7 @@ private final Origin origin; private final Map<String, String> rewritePrefix; + private final Set<String> dontRewritePrefix; private final Set<String> maintainPrefix; private final Map<String, Map<String, String>> rewriteDerivedPrefix; private final Map<DexType, DexType> emulatedInterfaces; @@ -205,6 +215,7 @@ origin, new HashMap<>(), Sets.newIdentityHashSet(), + Sets.newIdentityHashSet(), new HashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), @@ -223,6 +234,7 @@ Reporter reporter, Origin origin, Map<String, String> rewritePrefix, + Set<String> dontRewritePrefix, Set<String> maintainPrefix, Map<String, Map<String, String>> rewriteDerivedPrefix, Map<DexType, DexType> emulateLibraryInterface, @@ -239,6 +251,7 @@ this.reporter = reporter; this.origin = origin; this.rewritePrefix = new HashMap<>(rewritePrefix); + this.dontRewritePrefix = Sets.newHashSet(dontRewritePrefix); this.maintainPrefix = Sets.newHashSet(maintainPrefix); this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix); this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface); @@ -281,6 +294,11 @@ return this; } + public Builder putDontRewritePrefix(String prefix) { + dontRewritePrefix.add(prefix); + return this; + } + public Builder putMaintainPrefix(String prefix) { maintainPrefix.add(prefix); return this; @@ -385,6 +403,7 @@ validate(); return new HumanRewritingFlags( ImmutableMap.copyOf(rewritePrefix), + ImmutableSet.copyOf(dontRewritePrefix), ImmutableSet.copyOf(maintainPrefix), ImmutableMap.copyOf(rewriteDerivedPrefix), ImmutableMap.copyOf(emulatedInterfaces),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java index 69c82a9..ef8be02 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
@@ -4,24 +4,24 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.android.tools.r8.origin.Origin; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.Map; public class MultiAPILevelHumanDesugaredLibrarySpecification { private final Origin origin; private final HumanTopLevelFlags topLevelFlags; - private final Int2ObjectMap<HumanRewritingFlags> commonFlags; - private final Int2ObjectMap<HumanRewritingFlags> libraryFlags; - private final Int2ObjectMap<HumanRewritingFlags> programFlags; + private final Map<ApiLevelRange, HumanRewritingFlags> commonFlags; + private final Map<ApiLevelRange, HumanRewritingFlags> libraryFlags; + private final Map<ApiLevelRange, HumanRewritingFlags> programFlags; public MultiAPILevelHumanDesugaredLibrarySpecification( Origin origin, HumanTopLevelFlags topLevelFlags, - Int2ObjectMap<HumanRewritingFlags> commonFlags, - Int2ObjectMap<HumanRewritingFlags> libraryFlags, - Int2ObjectMap<HumanRewritingFlags> programFlags) { + Map<ApiLevelRange, HumanRewritingFlags> commonFlags, + Map<ApiLevelRange, HumanRewritingFlags> libraryFlags, + Map<ApiLevelRange, HumanRewritingFlags> programFlags) { this.origin = origin; this.topLevelFlags = topLevelFlags; this.commonFlags = commonFlags; @@ -37,28 +37,15 @@ return topLevelFlags; } - public Int2ObjectMap<HumanRewritingFlags> getCommonFlags() { + public Map<ApiLevelRange, HumanRewritingFlags> getCommonFlags() { return commonFlags; } - public Int2ObjectMap<HumanRewritingFlags> getLibraryFlags() { + public Map<ApiLevelRange, HumanRewritingFlags> getLibraryFlags() { return libraryFlags; } - public Int2ObjectMap<HumanRewritingFlags> getProgramFlags() { + public Map<ApiLevelRange, HumanRewritingFlags> getProgramFlags() { return programFlags; } - - public Map<Integer, HumanRewritingFlags> getCommonFlagsForTesting() { - return commonFlags; - } - - public Map<Integer, HumanRewritingFlags> getLibraryFlagsForTesting() { - return libraryFlags; - } - - public Map<Integer, HumanRewritingFlags> getProgramFlagsForTesting() { - return programFlags; - } - }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java index c059c27..6283419 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -8,10 +8,10 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.Reporter; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.IntArraySet; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; @@ -23,12 +23,12 @@ MultiAPILevelHumanDesugaredLibrarySpecification specification, Reporter reporter) { - IntArraySet apis = new IntArraySet(); + Set<ApiLevelRange> apis = new HashSet<>(); apis.addAll(specification.getCommonFlags().keySet()); apis.addAll(specification.getLibraryFlags().keySet()); apis.addAll(specification.getProgramFlags().keySet()); - for (Integer api : apis) { + for (ApiLevelRange api : apis) { deduplicateFlags(specification, reporter, api); } } @@ -36,11 +36,11 @@ private static void deduplicateFlags( MultiAPILevelHumanDesugaredLibrarySpecification specification, Reporter reporter, - int api) { + ApiLevelRange api) { - Int2ObjectMap<HumanRewritingFlags> commonFlags = specification.getCommonFlags(); - Int2ObjectMap<HumanRewritingFlags> libraryFlags = specification.getLibraryFlags(); - Int2ObjectMap<HumanRewritingFlags> programFlags = specification.getProgramFlags(); + Map<ApiLevelRange, HumanRewritingFlags> commonFlags = specification.getCommonFlags(); + Map<ApiLevelRange, HumanRewritingFlags> libraryFlags = specification.getLibraryFlags(); + Map<ApiLevelRange, HumanRewritingFlags> programFlags = specification.getProgramFlags(); HumanRewritingFlags library = libraryFlags.get(api); HumanRewritingFlags program = programFlags.get(api); @@ -68,7 +68,9 @@ } private static void putNewFlags( - int api, Int2ObjectMap<HumanRewritingFlags> flags, HumanRewritingFlags.Builder builder) { + ApiLevelRange api, + Map<ApiLevelRange, HumanRewritingFlags> flags, + HumanRewritingFlags.Builder builder) { HumanRewritingFlags build = builder.build(); if (build.isEmpty()) { flags.remove(api);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java index 928eeab..d77a0bc 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -7,6 +7,7 @@ import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY; import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.AMEND_LIBRARY_METHOD_KEY; import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_BELOW_OR_EQUAL_KEY; +import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_GREATER_OR_EQUAL_KEY; import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY; import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.COMMON_FLAGS_KEY; import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION; @@ -35,10 +36,9 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.google.gson.Gson; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -81,14 +81,17 @@ } private List<Object> rewritingFlagsToString( - Int2ObjectMap<HumanRewritingFlags> rewritingFlagsMap) { + Map<ApiLevelRange, HumanRewritingFlags> rewritingFlagsMap) { ArrayList<Object> list = new ArrayList<>(); - ArrayList<Integer> apis = new ArrayList<>(rewritingFlagsMap.keySet()); - apis.sort(Comparator.reverseOrder()); - for (int apiBelowOrEqual : apis) { - HumanRewritingFlags flags = rewritingFlagsMap.get(apiBelowOrEqual); + ArrayList<ApiLevelRange> apis = new ArrayList<>(rewritingFlagsMap.keySet()); + apis.sort((x, y) -> -x.deterministicOrder(y)); + for (ApiLevelRange range : apis) { + HumanRewritingFlags flags = rewritingFlagsMap.get(range); HashMap<String, Object> toJson = new LinkedHashMap<>(); - toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, apiBelowOrEqual); + toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, range.getApiLevelBelowOrEqualAsInt()); + if (range.hasApiLevelGreaterOrEqual()) { + toJson.put(API_LEVEL_GREATER_OR_EQUAL_KEY, range.getApiLevelGreaterOrEqualAsInt()); + } if (!flags.getRewritePrefix().isEmpty()) { toJson.put(REWRITE_PREFIX_KEY, new TreeMap<>(flags.getRewritePrefix())); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java index 9f9fa81..2eab9cf 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
@@ -6,11 +6,12 @@ import com.android.tools.r8.StringResource; import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.android.tools.r8.utils.Reporter; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import java.util.HashMap; +import java.util.Map; public class MultiAPILevelHumanDesugaredLibrarySpecificationParser extends HumanDesugaredLibrarySpecificationParser { @@ -27,26 +28,31 @@ HumanTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString, builder -> {}); - Int2ObjectMap<HumanRewritingFlags> commonFlags = parseAllFlags(COMMON_FLAGS_KEY); - Int2ObjectMap<HumanRewritingFlags> libraryFlags = parseAllFlags(LIBRARY_FLAGS_KEY); - Int2ObjectMap<HumanRewritingFlags> programFlags = parseAllFlags(PROGRAM_FLAGS_KEY); + Map<ApiLevelRange, HumanRewritingFlags> commonFlags = parseAllFlags(COMMON_FLAGS_KEY); + Map<ApiLevelRange, HumanRewritingFlags> libraryFlags = parseAllFlags(LIBRARY_FLAGS_KEY); + Map<ApiLevelRange, HumanRewritingFlags> programFlags = parseAllFlags(PROGRAM_FLAGS_KEY); return new MultiAPILevelHumanDesugaredLibrarySpecification( getOrigin(), topLevelFlags, commonFlags, libraryFlags, programFlags); } - private Int2ObjectMap<HumanRewritingFlags> parseAllFlags(String flagKey) { + private Map<ApiLevelRange, HumanRewritingFlags> parseAllFlags(String flagKey) { JsonElement jsonFlags = required(getJsonConfig(), flagKey); - Int2ObjectMap<HumanRewritingFlags> flags = new Int2ObjectArrayMap<>(); + Map<ApiLevelRange, HumanRewritingFlags> flags = new HashMap<>(); for (JsonElement jsonFlagSet : jsonFlags.getAsJsonArray()) { JsonObject flag = jsonFlagSet.getAsJsonObject(); - int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt(); + int apiLevelBelowOrEqual = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt(); + ApiLevelRange range = + flag.has(API_LEVEL_GREATER_OR_EQUAL_KEY) + ? new ApiLevelRange( + apiLevelBelowOrEqual, flag.get(API_LEVEL_GREATER_OR_EQUAL_KEY).getAsInt()) + : new ApiLevelRange(apiLevelBelowOrEqual); HumanRewritingFlags.Builder builder = - flags.containsKey(api_level_below_or_equal) - ? flags.get(api_level_below_or_equal).newBuilder(reporter(), getOrigin()) + flags.containsKey(range) + ? flags.get(range).newBuilder(reporter(), getOrigin()) : HumanRewritingFlags.builder(reporter(), getOrigin()); parseFlags(flag, builder); - flags.put(api_level_below_or_equal, builder.build()); + flags.put(range, builder.build()); } return flags; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java index a929fb9..8213473 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -175,7 +175,7 @@ DexMethod invokedMethod = cfInvoke.getMethod(); AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring(); MethodResolutionResult resolutionResult = - appInfo.resolveMethod(invokedMethod, cfInvoke.isInterface()); + appInfo.resolveMethodLegacy(invokedMethod, cfInvoke.isInterface()); if (!resolutionResult.isSingleResolution()) { return NO_REWRITING; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java index 6a32412..4ef9c00 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -35,7 +35,7 @@ @Override public String uniqueIdentifier() { - return "$retargeter$"; + return "$retargeter"; } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java index e1447fb..bfbf6a8 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -153,7 +153,7 @@ DexMethod forwardMethod = syntheticHelper.forwardingMethod(descriptor); assert forwardMethod != null && forwardMethod != target; DexEncodedMethod resolvedMethod = - appView.appInfoForDesugaring().resolveMethod(target, true).getResolvedMethod(); + appView.appInfoForDesugaring().resolveMethodLegacy(target, true).getResolvedMethod(); assert resolvedMethod != null; DexEncodedMethod desugaringForwardingMethod = DexEncodedMethod.createDesugaringForwardingMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java index 78fc59f..8cb5743 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -27,6 +27,7 @@ private final boolean libraryCompilation; private final Map<DexString, DexString> descriptorPrefix; private final Set<DexString> descriptorMaintainPrefix; + private final Set<DexString> descriptorDontRewritePrefix; private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix; private final Set<DexString> usedPrefix = Sets.newIdentityHashSet(); @@ -40,7 +41,8 @@ this.synthesizedPrefix = humanSpec.getSynthesizedLibraryClassesPackagePrefix(); this.libraryCompilation = humanSpec.isLibraryCompilation(); this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix()); - this.descriptorMaintainPrefix = convertMaintainPrefix(rewritingFlags.getMaintainPrefix()); + this.descriptorDontRewritePrefix = convertPrefixSet(rewritingFlags.getDontRewritePrefix()); + this.descriptorMaintainPrefix = convertPrefixSet(rewritingFlags.getMaintainPrefix()); this.descriptorDifferentPrefix = convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix()); } @@ -102,6 +104,11 @@ } private void registerClassType(DexType type) { + // TODO(b/222647019): To remove, the problem is that the prefix java.nio.channels.FileChannel + // matches java.nio.channels.FileChannel$MapMode. + if (type.toString().equals("java.nio.channels.FileChannel$MapMode")) { + return; + } registerType(type); registerMaintainType(type); registerDifferentType(type); @@ -110,6 +117,9 @@ private void registerType(DexType type) { DexType rewrittenType = rewrittenType(type); if (rewrittenType != null) { + if (prefixMatching(type, descriptorDontRewritePrefix) != null) { + return; + } builder.rewriteType(type, rewrittenType); } } @@ -174,7 +184,7 @@ return mapBuilder.build(); } - private ImmutableSet<DexString> convertMaintainPrefix(Set<String> maintainPrefix) { + private ImmutableSet<DexString> convertPrefixSet(Set<String> maintainPrefix) { ImmutableSet.Builder<DexString> builder = ImmutableSet.builder(); for (String prefix : maintainPrefix) { builder.add(toDescriptorPrefix(prefix));
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 9487210..18147b5 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,9 +12,12 @@ 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.utils.MethodSignatureEquivalence; +import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -22,6 +25,7 @@ public class HumanToMachineWrapperConverter { + private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get(); private final AppInfoWithClassHierarchy appInfo; private final Set<DexType> missingClasses = Sets.newIdentityHashSet(); @@ -56,6 +60,10 @@ private List<DexMethod> allImplementedMethods( DexClass wrapperClass, Set<DexMethod> excludedMethods) { + 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<>(); workList.add(wrapperClass); @@ -64,7 +72,7 @@ for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) { if (!virtualMethod.isPrivateMethod()) { assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod(); - boolean alreadyAdded = excludedMethods.contains(virtualMethod.getReference()); + 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) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java index bd26b562..baa4178 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -17,6 +17,7 @@ import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags; @@ -32,22 +33,27 @@ import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Pair; +import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.Timing; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; public class LegacyToHumanSpecificationConverter { private static final String WRAPPER_PREFIX = "__wrapper__."; private static final AndroidApiLevel LEGACY_HACK_LEVEL = AndroidApiLevel.N_MR1; private final Timing timing; + private final Set<String> missingClasses = new HashSet<>(); + private final Set<String> missingMethods = new HashSet<>(); public LegacyToHumanSpecificationConverter(Timing timing) { this.timing = timing; @@ -81,14 +87,15 @@ AppForSpecConversion.readAppForTesting(desugaredJDKLib, androidLib, options, true, timing); HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags()); - Int2ObjectArrayMap<HumanRewritingFlags> commonFlags = + Map<ApiLevelRange, HumanRewritingFlags> commonFlags = convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin); - Int2ObjectArrayMap<HumanRewritingFlags> programFlags = + Map<ApiLevelRange, HumanRewritingFlags> programFlags = convertRewritingFlagMap(legacySpec.getProgramFlags(), app, origin); - Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags = + Map<ApiLevelRange, HumanRewritingFlags> libraryFlags = convertRewritingFlagMap(legacySpec.getLibraryFlags(), app, origin); legacyLibraryFlagHacks(libraryFlags, app, origin); + reportWarnings(app.options.reporter); MultiAPILevelHumanDesugaredLibrarySpecification humanSpec = new MultiAPILevelHumanDesugaredLibrarySpecification( @@ -135,16 +142,34 @@ humanRewritingFlags = builder.build(); timing.end(); } - + reportWarnings(app.options.reporter); timing.end(); return new HumanDesugaredLibrarySpecification( humanTopLevelFlags, humanRewritingFlags, legacySpec.isLibraryCompilation()); } + private void reportWarnings(Reporter reporter) { + String errorSdk = "This usually means that the compilation SDK is absent or too old."; + if (!missingClasses.isEmpty()) { + reporter.warning( + "Cannot retarget core lib member for missing classes: " + + missingClasses + + ". " + + errorSdk); + } + if (!missingMethods.isEmpty()) { + reporter.warning( + "Should have found a method (library specifications) for " + + missingMethods + + ". " + + errorSdk); + } + } + private void legacyLibraryFlagHacks( - Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) { - int level = LEGACY_HACK_LEVEL.getLevel(); - HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level); + Map<ApiLevelRange, HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) { + ApiLevelRange range = new ApiLevelRange(LEGACY_HACK_LEVEL.getLevel()); + HumanRewritingFlags humanRewritingFlags = libraryFlags.get(range); if (humanRewritingFlags == null) { // Skip CHM only configuration. return; @@ -152,7 +177,7 @@ HumanRewritingFlags.Builder builder = humanRewritingFlags.newBuilder(app.options.reporter, origin); legacyLibraryFlagHacks(app.dexItemFactory(), builder); - libraryFlags.put(level, builder.build()); + libraryFlags.put(range, builder.build()); } private void legacyLibraryFlagHacks( @@ -186,10 +211,11 @@ builder.retargetMethod(source, target); } - private Int2ObjectArrayMap<HumanRewritingFlags> convertRewritingFlagMap( + private Map<ApiLevelRange, HumanRewritingFlags> convertRewritingFlagMap( Int2ObjectMap<LegacyRewritingFlags> libFlags, DexApplication app, Origin origin) { - Int2ObjectArrayMap<HumanRewritingFlags> map = new Int2ObjectArrayMap<>(); - libFlags.forEach((key, flags) -> map.put((int) key, convertRewritingFlags(flags, app, origin))); + Map<ApiLevelRange, HumanRewritingFlags> map = new HashMap<>(); + libFlags.forEach( + (key, flags) -> map.put(new ApiLevelRange(key), convertRewritingFlags(flags, app, origin))); return map; } @@ -257,7 +283,11 @@ typeMap.forEach( (type, rewrittenType) -> { DexClass dexClass = app.definitionFor(type); - assert dexClass != null; + if (dexClass == null) { + assert false : "Cannot retarget core lib member for missing class " + type; + missingClasses.add(type.toSourceString()); + return; + } List<DexClassAndMethod> methodsWithName = findMethodsWithName(name, dexClass, builder, app); for (DexClassAndMethod dexClassAndMethod : methodsWithName) { @@ -297,12 +327,11 @@ DexEncodedMethod.builder().setMethod(method).setAccessFlags(flags).build(); return ImmutableList.of(DexClassAndMethod.create(clazz, build)); } - assert !found.isEmpty() - : "Should have found a method (library specifications) for " - + clazz.toSourceString() - + "." - + methodName - + ". Maybe the library used for the compilation should be newer."; + if (found.isEmpty()) { + String warning = clazz.toSourceString() + "." + methodName; + assert false : "Should have found a method (library specifications) for " + warning; + missingMethods.add(warning); + } return found; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java index 5c89290..9630887 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -70,7 +70,7 @@ CfInvoke invoke = instruction.asInvoke(); DexMethod invokedMethod = invoke.getMethod(); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethod(invokedMethod, invoke.isInterface()); + appView.appInfo().resolveMethodLegacy(invokedMethod, invoke.isInterface()); if (shouldRewriteInvokeToThrow(invoke, resolutionResult)) { return computeInvokeAsThrowRewrite(appView, invoke, resolutionResult); }
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 266b1c5..0806685 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
@@ -646,7 +646,7 @@ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring(); for (Wrapper<DexMethod> signature : emulatedInterfaceInfo.signatures.signatures) { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnClass(clazz, signature.get()); + appInfo.resolveMethodOnClassLegacy(clazz, signature.get()); if (resolutionResult.isFailedResolution()) { return true; } @@ -685,7 +685,7 @@ private void resolveForwardForSignature( DexClass clazz, DexMethod method, BiConsumer<DexClassAndMethod, DexMethod> addForward) { AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring(); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnLegacy(clazz, method); if (resolutionResult.isFailedResolution() || resolutionResult.asSuccessfulMemberResolutionResult().getResolvedMember().isStatic()) { // When doing resolution we may find a static or private targets and bubble up the failed @@ -702,7 +702,7 @@ resolutionResult.asSuccessfulMemberResolutionResult().getResolvedMember().isStatic()); } if (staticTarget.isAssigned() && staticTarget.isTrue()) { - resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method); + resolutionResult = appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); } if (resolutionResult.isFailedResolution()) { if (resolutionResult.isIncompatibleClassChangeErrorResult()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java index 3919202..eda530c 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -263,7 +263,10 @@ } assert verifyKind(method, kinds -> kinds.COMPANION_CLASS); DexClassAndMethod resolvedMethod = - appView.appInfoForDesugaring().resolveMethod(method.getMethod(), true).getResolutionPair(); + appView + .appInfoForDesugaring() + .resolveMethodLegacy(method.getMethod(), true) + .getResolutionPair(); return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod).getReference(); } @@ -274,7 +277,7 @@ DexClassAndMethod method = appView .appInfoForDesugaring() - .resolveMethod(emulatedDispatchMethod.getMethod(), true) + .resolveMethodLegacy(emulatedDispatchMethod.getMethod(), true) .getResolutionPair(); assert verifyKind(emulatedDispatchMethod, kinds -> kinds.EMULATED_INTERFACE_CLASS); if (method.isProgramMethod()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java index 7b3bb07..bc8ebd0 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -307,7 +307,7 @@ // would change behavior from throwing ICCE to dispatching to the companion class method. AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring(); MethodResolutionResult resolution = - appInfo.resolveMethod(invoke.getMethod(), invoke.isInterface()); + appInfo.resolveMethodLegacy(invoke.getMethod(), invoke.isInterface()); if (!resolution.isSingleResolution() || !resolution.asSingleResolution().getResolvedMethod().isStatic()) { return DesugarDescription.nothing(); @@ -408,7 +408,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfoForDesugaring() - .resolveMethodOnInterface(holder, invoke.getMethod()) + .resolveMethodOnInterfaceLegacy(holder, invoke.getMethod()) .asSingleResolution(); if (holder.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, true)) { return computeInvokeAsThrowRewrite(invoke, resolutionResult, context); @@ -437,7 +437,7 @@ AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring(); SingleResolutionResult<?> resolution = appInfoForDesugaring - .resolveMethod(invoke.getMethod(), invoke.isInterface()) + .resolveMethodLegacy(invoke.getMethod(), invoke.isInterface()) .asSingleResolution(); if (resolution != null && resolution.getResolvedMethod().isPrivate() @@ -454,7 +454,9 @@ private DesugarDescription computeEmulatedInterfaceVirtualDispatchOrNull(CfInvoke invoke) { MethodResolutionResult resolutionResult = - appView.appInfoForDesugaring().resolveMethod(invoke.getMethod(), invoke.isInterface()); + appView + .appInfoForDesugaring() + .resolveMethodLegacy(invoke.getMethod(), invoke.isInterface()); DerivedMethod emulatedDispatchMethod = helper.computeEmulatedInterfaceDispatchMethod(resolutionResult); if (emulatedDispatchMethod == null) { @@ -492,7 +494,7 @@ } MethodResolutionResult resolution = - appView.appInfoForDesugaring().resolveMethod(invokedMethod, invoke.isInterface()); + appView.appInfoForDesugaring().resolveMethodLegacy(invokedMethod, invoke.isInterface()); if (resolution.isFailedResolution()) { return computeInvokeAsThrowRewrite(invoke, null, context); } @@ -638,7 +640,10 @@ } SingleResolutionResult<?> resolutionResult = - appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution(); + appView + .appInfoForDesugaring() + .resolveMethodOnLegacy(clazz, invokedMethod) + .asSingleResolution(); if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, false)) { return computeInvokeAsThrowRewrite(invoke, resolutionResult, context); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java index b8b50d9..6a57dae 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -114,7 +114,7 @@ @Override public String uniqueIdentifier() { - return "$emulatedInterface$"; + return "$emulatedInterface"; } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java index 6a84f09..366ee34 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -476,7 +476,7 @@ @Override public String uniqueIdentifier() { - return "$record$"; + return "$record"; } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java index 72b8d6a..1961b30 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -32,12 +32,14 @@ import com.android.tools.r8.utils.LazyBox; import com.android.tools.r8.utils.ThrowingCharIterator; import com.android.tools.r8.utils.Timing; -import com.android.tools.r8.utils.WorkList; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; import java.io.UTFDataFormatException; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class AssertionsRewriter { @@ -274,6 +276,7 @@ * } * } * </pre> + * * For Kotlin the Class instance method desiredAssertionStatus() is only called for the class * kotlin._Assertions, where kotlin._Assertions.class.desiredAssertionStatus() is read into the * static field kotlin._Assertions.ENABLED. @@ -311,7 +314,8 @@ * } * </pre> * - * With the rewriting below and AssertionTransformation.DISABLE (and other rewritings) the resulting code is: + * With the rewriting below and AssertionTransformation.DISABLE (and other rewritings) the + * resulting code is: * * <pre> * class A { @@ -332,23 +336,26 @@ * } * } * </pre> - + * * NOTE: that in Kotlin the assertion condition is always calculated. So it is still present in * the code and even for AssertionTransformation.DISABLE. */ - public void run(DexEncodedMethod method, IRCode code, Timing timing) { + public void run( + DexEncodedMethod method, IRCode code, DeadCodeRemover deadCodeRemover, Timing timing) { if (enabled) { timing.begin("Rewrite assertions"); - runInternal(method, code); + if (runInternal(method, code)) { + deadCodeRemover.run(code, timing); + } assert code.isConsistentSSA(appView); timing.end(); } } - private void runInternal(DexEncodedMethod method, IRCode code) { + private boolean runInternal(DexEncodedMethod method, IRCode code) { ConfigurationEntryWithDexString configuration = getTransformationForMethod(method); if (configuration.isPassthrough()) { - return; + return false; } DexEncodedMethod clinit; // If the <clinit> of this class did not have have code to turn on assertions don't try to @@ -358,20 +365,25 @@ } else { DexClass clazz = appView.definitionFor(method.getHolderType()); if (clazz == null) { - return; + return false; } clinit = clazz.getClassInitializer(); } // For the transformation to rewrite the throw with a callback collect information on the // blocks covered by the if (!$assertionsDisabled or ENABLED) condition together with weather // the assertion handling is on the true or false branch. - Map<If, Boolean> assertionEntryIfs = new IdentityHashMap<>(); + Map<If, Boolean> assertionEntryIfs = new Reference2BooleanOpenHashMap<>(); Map<Throw, BasicBlock> throwSuccessorAfterHandler = new IdentityHashMap<>(); + Set<BasicBlock> assertionBlocks = Sets.newIdentityHashSet(); + Map<If, Boolean> additionalAssertionsEnabledIfs = new Reference2BooleanOpenHashMap<>(); if (configuration.isAssertionHandler()) { LazyBox<DominatorTree> dominatorTree = new LazyBox<>(() -> new DominatorTree(code)); code.getBlocks() .forEach( basicBlock -> { + if (assertionBlocks.contains(basicBlock)) { + return; + } If theIf = isCheckAssertionsEnabledBlock(basicBlock); if (theIf != null) { // All blocks dominated by the if is the assertion code. For Java it is on the @@ -382,25 +394,42 @@ theIf.lhs().getDefinition().asStaticGet()); BasicBlock assertionBlockEntry = theIf.targetFromBoolean(conditionForAssertionBlock); - List<BasicBlock> blocks = + List<BasicBlock> dominatedBlocks = dominatorTree.computeIfAbsent().dominatedBlocks(assertionBlockEntry); - Throw throwInstruction = isAlwaysThrowingEntry(assertionBlockEntry, blocks); + Throw throwInstruction = + dominatedBlocksHasSingleThrow(assertionBlockEntry, dominatedBlocks); if (throwInstruction != null) { assertionEntryIfs.put(theIf, conditionForAssertionBlock); throwSuccessorAfterHandler.put( throwInstruction, theIf.targetFromBoolean(!conditionForAssertionBlock)); + // Collect any additional assertions enabled checks dominated by the current + // assertions entry check. + dominatedBlocks.forEach( + block -> { + If additionalAssertionsEnabledIf = isCheckAssertionsEnabledBlock(block); + if (additionalAssertionsEnabledIf != null) { + additionalAssertionsEnabledIfs.put( + additionalAssertionsEnabledIf, + !isUsingJavaAssertionsDisabledField( + additionalAssertionsEnabledIf + .lhs() + .getDefinition() + .asStaticGet())); + } + }); + assertionBlocks.addAll(dominatedBlocks); } } }); } assert assertionEntryIfs.size() == throwSuccessorAfterHandler.size(); - // For javac generated code it is assumed that the code in <clinit> will tell if the code // in other methods of the class can have assertion checks. boolean isInitializerEnablingJavaVmAssertions = clinit != null && clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions(); // This code will process the assertion code in all methods including <clinit>. InstructionListIterator iterator = code.instructionListIterator(); + boolean needsDeadCodeRemoval = false; while (iterator.hasNext()) { Instruction current = iterator.next(); if (current.isInvokeMethod()) { @@ -446,11 +475,12 @@ if (current.isIf()) { If ifInstruction = current.asIf(); if (assertionEntryIfs.containsKey(ifInstruction)) { - ifInstruction - .targetFromBoolean(!assertionEntryIfs.get(ifInstruction)) - .unlinkSinglePredecessorSiblingsAllowed(); - ifInstruction.lhs().removeUser(ifInstruction); - iterator.replaceCurrentInstruction(new Goto()); + forceAssertionsEnabled(ifInstruction, assertionEntryIfs, iterator); + needsDeadCodeRemoval = true; + } + if (additionalAssertionsEnabledIfs.containsKey(ifInstruction)) { + forceAssertionsEnabled(ifInstruction, additionalAssertionsEnabledIfs, iterator); + needsDeadCodeRemoval = true; } } else if (current.isThrow()) { Throw throwInstruction = current.asThrow(); @@ -469,6 +499,7 @@ } } } + return needsDeadCodeRemoval; } private void rewriteKotlinAssertionEnable( @@ -541,15 +572,9 @@ : null; } - private Throw isAlwaysThrowingEntry(BasicBlock block, List<BasicBlock> blocks) { - WorkList<BasicBlock> workList = WorkList.newIdentityWorkList(block); + private Throw dominatedBlocksHasSingleThrow(BasicBlock block, List<BasicBlock> dominatedBlocks) { Throw theThrow = null; - while (workList.hasNext()) { - BasicBlock current = workList.next(); - workList.addIfNotSeen(current.getNormalSuccessors()); - if (!blocks.containsAll(current.getNormalSuccessors())) { - return null; - } + for (BasicBlock current : dominatedBlocks) { if (current.exit().isReturn()) { return null; } @@ -562,4 +587,13 @@ } return theThrow; } + + private void forceAssertionsEnabled( + If ifInstruction, Map<If, Boolean> targetMap, InstructionListIterator iterator) { + ifInstruction + .targetFromBoolean(!targetMap.get(ifInstruction)) + .unlinkSinglePredecessorSiblingsAllowed(); + ifInstruction.lhs().removeUser(ifInstruction); + iterator.replaceCurrentInstruction(new Goto()); + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java index 1c49fc9..df9d508 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -230,7 +230,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .unsafeResolveMethodDueToDexFormatLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult == null) { return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java index 86578a2..3d9d2b8 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -98,11 +98,11 @@ import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOutputMode; +import com.android.tools.r8.utils.LazyBox; import com.android.tools.r8.utils.LongInterval; import com.android.tools.r8.utils.SetUtils; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; -import com.google.common.base.Suppliers; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -145,7 +145,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; public class CodeRewriter { @@ -1838,8 +1837,7 @@ // and ConstStrings with one user. // TODO(ager): Generalize this to shorten live ranges for more instructions? Currently // doing so seems to make things worse. - Supplier<DominatorTree> dominatorTreeMemoization = - Suppliers.memoize(() -> new DominatorTree(code)); + LazyBox<DominatorTree> dominatorTreeMemoization = new LazyBox<>(() -> new DominatorTree(code)); Map<BasicBlock, Map<Value, Instruction>> addConstantInBlock = new IdentityHashMap<>(); LinkedList<BasicBlock> blocks = code.blocks; for (BasicBlock block : blocks) { @@ -1945,7 +1943,7 @@ private void shortenLiveRangesInsideBlock( IRCode code, BasicBlock block, - Supplier<DominatorTree> dominatorTreeMemoization, + LazyBox<DominatorTree> dominatorTreeMemoization, Map<BasicBlock, Map<Value, Instruction>> addConstantInBlock, Predicate<ConstInstruction> selector) { InstructionListIterator iterator = block.listIterator(code); @@ -2014,7 +2012,7 @@ userBlocks.add(phi.getBlock()); } // Locate the closest dominator block for all user blocks. - DominatorTree dominatorTree = dominatorTreeMemoization.get(); + DominatorTree dominatorTree = dominatorTreeMemoization.computeIfAbsent(); BasicBlock dominator = dominatorTree.closestDominator(userBlocks); // If the closest dominator block is a block that uses the constant for a phi the constant // needs to go in the immediate dominator block so that it is available for phi moves. @@ -2816,9 +2814,9 @@ return; } - Supplier<Long2ReferenceMap<List<ConstNumber>>> constantsByValue = - Suppliers.memoize(() -> getConstantsByValue(code)); - Supplier<DominatorTree> dominatorTree = Suppliers.memoize(() -> new DominatorTree(code)); + LazyBox<Long2ReferenceMap<List<ConstNumber>>> constantsByValue = + new LazyBox<>(() -> getConstantsByValue(code)); + LazyBox<DominatorTree> dominatorTree = new LazyBox<>(() -> new DominatorTree(code)); boolean changed = false; for (BasicBlock block : code.blocks) { @@ -2908,7 +2906,7 @@ } } - if (constantsByValue.get().isEmpty()) { + if (constantsByValue.computeIfAbsent().isEmpty()) { break; } } @@ -2954,16 +2952,17 @@ long withValue, Value newValue, BasicBlock dominator, - Supplier<Long2ReferenceMap<List<ConstNumber>>> constantsByValueSupplier, + LazyBox<Long2ReferenceMap<List<ConstNumber>>> constantsByValueSupplier, IRCode code, - Supplier<DominatorTree> dominatorTree) { + LazyBox<DominatorTree> dominatorTree) { if (newValue.hasLocalInfo()) { // We cannot replace a constant with a value that has local info, because that could change // debugging behavior. return false; } - Long2ReferenceMap<List<ConstNumber>> constantsByValue = constantsByValueSupplier.get(); + Long2ReferenceMap<List<ConstNumber>> constantsByValue = + constantsByValueSupplier.computeIfAbsent(); List<ConstNumber> constantsWithValue = constantsByValue.get(withValue); if (constantsWithValue == null || constantsWithValue.isEmpty()) { return false; @@ -3003,7 +3002,7 @@ continue; } - if (dominatorTree.get().dominatedBy(block, dominator)) { + if (dominatorTree.computeIfAbsent().dominatedBy(block, dominator)) { if (newValue.getType().lessThanOrEqual(value.getType(), appView)) { value.replaceUsers(newValue); block.listIterator(code, constNumber).removeOrReplaceByDebugLocalRead();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java index 9ba2beb..5930cd3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -369,7 +369,10 @@ } SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOnClass(target.getHolderType(), target).asSingleResolution(); + appView + .appInfo() + .resolveMethodOnClassLegacy(target.getHolderType(), target) + .asSingleResolution(); if (resolutionResult == null || resolutionResult .isAccessibleForVirtualDispatchFrom(context, appView.appInfo()) @@ -385,7 +388,7 @@ } SingleResolutionResult<?> newResolutionResult = - appView.appInfo().resolveMethodOnClass(receiverType, target).asSingleResolution(); + appView.appInfo().resolveMethodOnClassLegacy(receiverType, target).asSingleResolution(); if (newResolutionResult == null || newResolutionResult .isAccessibleForVirtualDispatchFrom(context, appView.appInfo())
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java index 265d097..257af5e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -139,7 +139,7 @@ SingleResolutionResult<?> resolutionResult = appInfoWithLiveness - .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit()) + .resolveMethodLegacy(invoke.getInvokedMethod(), invoke.getInterfaceBit()) .asSingleResolution(); if (resolutionResult == null || resolutionResult
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index 4ca07c5..4be71a5 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -963,7 +963,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethod(invokedMethod, invoke.getInterfaceBit()) + .resolveMethodLegacy(invokedMethod, invoke.getInterfaceBit()) .asSingleResolution(); if (resolutionResult == null || resolutionResult.isAccessibleFrom(context, appView).isPossiblyFalse()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java index 4cf1c8c..59e06c4 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -175,7 +175,7 @@ return ConstraintWithTarget.ALWAYS; } MethodResolutionResult resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup); + appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(lookup); DexEncodedMethod target = singleTargetWhileVerticalClassMerging( resolutionResult, context, MethodResolutionResult::lookupInvokeDirectTarget); @@ -207,7 +207,7 @@ return ConstraintWithTarget.ALWAYS; } MethodResolutionResult resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup); + appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(lookup); DexEncodedMethod target = singleTargetWhileVerticalClassMerging( resolutionResult, context, MethodResolutionResult::lookupInvokeStaticTarget); @@ -364,7 +364,8 @@ // Perform resolution and derive inlining constraints based on the accessibility of the // resolution result. - MethodResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, isInterface); + MethodResolutionResult resolutionResult = + appView.appInfo().resolveMethodLegacy(method, isInterface); if (!resolutionResult.isVirtualTarget()) { return ConstraintWithTarget.NEVER; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java index 09b614e..d572d2f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -290,7 +290,10 @@ } SingleResolutionResult<?> resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution(); + appView + .appInfo() + .unsafeResolveMethodDueToDexFormatLegacy(invokedMethod) + .asSingleResolution(); if (resolutionResult == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java index 1a35898..e111ec0 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
@@ -91,7 +91,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit()) + .resolveMethodLegacy(invoke.getInvokedMethod(), invoke.getInterfaceBit()) .asSingleResolution(); if (resolutionResult == null || resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java index c82a122..e55a1b0 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/NaturalIntLoopRemover.java
@@ -30,7 +30,7 @@ public class NaturalIntLoopRemover { public void run(AppView<?> appView, IRCode code) { - if (!appView.testing().enableExperimentalLoopUnrolling) { + if (!appView.options().enableLoopUnrolling) { return; } boolean loopRemoved = false; @@ -87,6 +87,9 @@ if (!analyzeLoopExit(loopBody, comparison, builder)) { return false; } + if (!analyzePhiUses(loopBody, comparison, builder)) { + return false; + } NaturalIntLoopWithKnowIterations loop = builder.build(); @@ -98,6 +101,42 @@ } /** + * The loop unroller removes phis corresponding to the loop backjump. There are three scenarios: + * (1) The loop has a single exit point analyzed, phis used outside the loop are replaced by the + * value at the end of the loop body. + * (2) The phis are unused outside the loop, and they are simply removed. + * (3) The loop has multiple exits and the phis are used outside the loop, this would require + * dealing with complex merge point and postponing phis after the loop, we bail out. + */ + private boolean analyzePhiUses( + Set<BasicBlock> loopBody, If comparison, NaturalIntLoopWithKnowIterations.Builder builder) { + // Check for single exit scenario. + Set<BasicBlock> successors = Sets.newIdentityHashSet(); + for (BasicBlock basicBlock : loopBody) { + successors.addAll(basicBlock.getSuccessors()); + } + successors.removeAll(loopBody); + if (successors.size() == 1) { + assert successors.iterator().next() == builder.getLoopExit(); + return true; + } + // Check phis are unused outside the loop. + for (Phi phi : comparison.getBlock().getPhis()) { + for (Instruction use : phi.uniqueUsers()) { + if (!loopBody.contains(use.getBlock())) { + return false; + } + } + for (Phi phiUse : phi.uniquePhiUsers()) { + if (!loopBody.contains(phiUse.getBlock())) { + return false; + } + } + } + return true; + } + + /** * Verifies the loop is well formed: the comparison on the int iterator should jump to a loop exit * on one side and to the loop body on the other side. */ @@ -305,6 +344,10 @@ this.loopBodyEntry = loopBodyEntry; } + public BasicBlock getLoopExit() { + return loopExit; + } + public void setLoopBody(Set<BasicBlock> loopBody) { this.loopBody = loopBody; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java index 5966a8c..a3c270a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -512,7 +512,7 @@ appView .appInfo() .withClassHierarchy() - .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .unsafeResolveMethodDueToDexFormatLegacy(invoke.getInvokedMethod()) .getResolvedProgramMethod(); if (resolvedMethod != null) { markClassAsInitialized(resolvedMethod.getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java index 20b26b0..afed4c2 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/RuntimeWorkaroundCodeRewriter.java
@@ -23,13 +23,12 @@ import com.android.tools.r8.ir.code.NumericType; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.utils.InternalOptions; -import com.google.common.base.Suppliers; +import com.android.tools.r8.utils.LazyBox; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import java.util.Collections; import java.util.ListIterator; -import java.util.function.Supplier; public class RuntimeWorkaroundCodeRewriter { @@ -87,8 +86,8 @@ return; } DexItemFactory factory = options.itemFactory; - final Supplier<DexMethod> javaLangLangSignum = - Suppliers.memoize( + LazyBox<DexMethod> javaLangLangSignum = + new LazyBox<>( () -> factory.createMethod( factory.createString("Ljava/lang/Long;"), @@ -118,7 +117,7 @@ Value longValue = firstMaterializing.inValues().get(0); InvokeStatic invokeLongSignum = new InvokeStatic( - javaLangLangSignum.get(), null, Collections.singletonList(longValue)); + javaLangLangSignum.computeIfAbsent(), null, Collections.singletonList(longValue)); ensureThrowingInstructionBefore(code, firstMaterializing, it, invokeLongSignum); return; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java index 75e94fb..9027ee1 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -219,16 +219,20 @@ result = ResultState.NOT_SATISFIED; } - public void addSatisfyingInstruction(Instruction instruction) { + public SimpleEffectAnalysisResultBuilder addSatisfyingInstruction(Instruction instruction) { satisfyingInstructions.add(instruction); + return this; } - public void setFailingBlocksForPartialResults(List<BasicBlock> basicBlocks) { + public SimpleEffectAnalysisResultBuilder setFailingBlocksForPartialResults( + List<BasicBlock> basicBlocks) { this.failingBlocksForPartialResults = basicBlocks; + return this; } - public void setResult(ResultState result) { + public SimpleEffectAnalysisResultBuilder setResult(ResultState result) { this.result = result; + return this; } public SimpleEffectAnalysisResult build() { @@ -246,63 +250,69 @@ public static SimpleEffectAnalysisResult run(IRCode code, InstructionAnalysis analysis) { SimpleEffectAnalysisResultBuilder builder = SimpleEffectAnalysisResult.builder(); IntBox visitedInstructions = new IntBox(); - new StatefulDepthFirstSearchWorkList<BasicBlock, ResultStateWithPartialBlocks>() { + TraversalContinuation<Void, ResultStateWithPartialBlocks> runResult = + new StatefulDepthFirstSearchWorkList<BasicBlock, ResultStateWithPartialBlocks, Void>() { - @Override - protected TraversalContinuation<?, ?> process( - DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node, - Function<BasicBlock, DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>> - childNodeConsumer) { - InstructionEffect effect = NO_EFFECT; - for (Instruction instruction : node.getNode().getInstructions()) { - if (visitedInstructions.getAndIncrement() > analysis.maxNumberOfInstructions()) { - builder.fail(); - return doBreak(); - } - effect = analysis.analyze(instruction); - if (!effect.isNoEffect()) { - if (effect.isDesired()) { - builder.addSatisfyingInstruction(instruction); + @Override + protected TraversalContinuation<Void, ResultStateWithPartialBlocks> process( + DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node, + Function<BasicBlock, DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>> + childNodeConsumer) { + InstructionEffect effect = NO_EFFECT; + for (Instruction instruction : node.getNode().getInstructions()) { + if (visitedInstructions.getAndIncrement() > analysis.maxNumberOfInstructions()) { + return doBreak(); + } + effect = analysis.analyze(instruction); + if (!effect.isNoEffect()) { + if (effect.isDesired()) { + builder.addSatisfyingInstruction(instruction); + } + break; + } } - break; - } - } - if (effect.isNoEffect()) { - List<BasicBlock> successors = analysis.getSuccessors(node.getNode()); - for (BasicBlock successor : successors) { - DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> childNode = - childNodeConsumer.apply(successor); - if (childNode.hasState()) { - // If we see a block where the children have not been processed we cannot guarantee - // all paths having the effect since - ex. we could have a non-terminating loop. - builder.fail(); - return doBreak(); + if (effect.isNoEffect()) { + List<BasicBlock> successors = analysis.getSuccessors(node.getNode()); + for (BasicBlock successor : successors) { + DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> childNode = + childNodeConsumer.apply(successor); + if (childNode.hasState()) { + // If we see a block where the children have not been processed we cannot + // guarantee all paths having the effect since - ex. we could have a + // non-terminating loop. + return doBreak(); + } + } } + node.setState( + new ResultStateWithPartialBlocks(effect.toResultState(), ImmutableList.of())); + return doContinue(); } - } - node.setState(new ResultStateWithPartialBlocks(effect.toResultState(), ImmutableList.of())); - return doContinue(); - } - @Override - protected TraversalContinuation<?, ?> joiner( - DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node, - List<DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>> childNodes) { - ResultStateWithPartialBlocks resultState = node.getState(); - if (resultState.state.isNotComputed()) { - resultState = resultState.joinChildren(childNodes); - } else { - assert resultState.state.isSatisfied() || resultState.state.isNotSatisfied(); - assert childNodes.isEmpty(); - } - node.setState(resultState); - if (node.getNode().isEntry()) { - builder.setResult(resultState.state); - builder.setFailingBlocksForPartialResults(resultState.failingBlocks); - } - return doContinue(); - } - }.run(code.entryBlock()); + @Override + protected TraversalContinuation<Void, ResultStateWithPartialBlocks> joiner( + DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks> node, + List<DFSNodeWithState<BasicBlock, ResultStateWithPartialBlocks>> childNodes) { + ResultStateWithPartialBlocks resultState = node.getState(); + if (resultState.state.isNotComputed()) { + resultState = resultState.joinChildren(childNodes); + } else { + assert resultState.state.isSatisfied() || resultState.state.isNotSatisfied(); + assert childNodes.isEmpty(); + } + node.setState(resultState); + return doContinue(resultState); + } + }.run(code.entryBlock()); + + if (runResult.isBreak()) { + builder.fail(); + } else { + ResultStateWithPartialBlocks resultState = runResult.asContinue().getValue(); + builder + .setResult(resultState.state) + .setFailingBlocksForPartialResults(resultState.failingBlocks); + } return builder.build(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java index ccbd132..7607596 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -29,13 +29,13 @@ import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider; import com.android.tools.r8.ir.optimize.string.StringOptimizer; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.LazyBox; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; public final class ClassInliner { @@ -135,7 +135,7 @@ MethodProcessor methodProcessor, MethodProcessingContext methodProcessingContext, Inliner inliner, - Supplier<InliningOracle> defaultOracle) { + LazyBox<InliningOracle> defaultOracle) { // Collect all the new-instance and static-get instructions in the code before inlining. List<Instruction> roots =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index 0b876a7..10cdd99 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -65,6 +65,7 @@ import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter; import com.android.tools.r8.kotlin.KotlinClassLevelInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.LazyBox; import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.Timing; @@ -79,7 +80,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.Function; -import java.util.function.Supplier; final class InlineCandidateProcessor { @@ -218,7 +218,7 @@ * * @return null if all users are eligible, or the first ineligible user. */ - InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) { + InstructionOrPhi areInstanceUsersEligible(LazyBox<InliningOracle> defaultOracle) { // No Phi users. if (eligibleInstance.hasPhiUsers()) { return eligibleInstance.firstPhiUser(); // Not eligible. @@ -294,7 +294,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethod(invoke.getInvokedMethod(), invoke.getInterfaceBit()) + .resolveMethodLegacy(invoke.getInvokedMethod(), invoke.getInterfaceBit()) .asSingleResolution(); if (resolutionResult == null || resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) { @@ -1028,7 +1028,7 @@ // signature of the invocation resolves to a private or static method. // TODO(b/147212189): Why not inline private methods? If access is permitted it is valid. MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnClass(eligibleClass, callee); + appView.appInfo().resolveMethodOnClassLegacy(eligibleClass, callee); if (resolutionResult.isSingleResolution() && !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) { return false; @@ -1073,7 +1073,7 @@ InvokeMethod invoke, SingleResolutionResult<?> resolutionResult, ProgramMethod singleTarget, - Supplier<InliningOracle> defaultOracle, + LazyBox<InliningOracle> defaultOracle, Set<Instruction> indirectUsers) { if (!((invoke.isInvokeDirect() && !invoke.isInvokeConstructor(dexItemFactory)) || invoke.isInvokeInterface() @@ -1092,7 +1092,7 @@ } // Check if the method is inline-able by standard inliner. - InliningOracle oracle = defaultOracle.get(); + InliningOracle oracle = defaultOracle.computeIfAbsent(); if (!oracle.passesInliningConstraints( invoke, resolutionResult, @@ -1176,7 +1176,10 @@ NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty(); for (DexMethod invokedMethod : nonEmptyUsage.getMethodCallsWithParameterAsReceiver()) { SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).asSingleResolution(); + appView + .appInfo() + .resolveMethodOnLegacy(eligibleClass, invokedMethod) + .asSingleResolution(); if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) { return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java index ebad797..e4bbb6c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -250,7 +250,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnClassHolder(invoke.getInvokedMethod()) + .resolveMethodOnClassHolderLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult == null) { return state.abandonClassInliningInCurrentContexts(receiverRoot); @@ -289,7 +289,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnInterfaceHolder(invoke.getInvokedMethod()) + .resolveMethodOnInterfaceHolderLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult == null) { return state.abandonClassInliningInCurrentContexts(receiverRoot); @@ -305,7 +305,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .unsafeResolveMethodDueToDexFormatLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult != null && resolutionResult.getResolvedMethod().getReference() @@ -331,7 +331,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnClassHolder(invoke.getInvokedMethod()) + .resolveMethodOnClassHolderLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult == null) { return state.abandonClassInliningInCurrentContexts(receiverRoot);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java index cfec469..ed028ee 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -461,7 +461,7 @@ ProgramMethod checkNotZeroMethod = appView .appInfo() - .resolveMethodOnClassHolder(checkNotZeroMethodReference) + .resolveMethodOnClassHolderLegacy(checkNotZeroMethodReference) .getResolvedProgramMethod(); if (checkNotZeroMethod != null) { EnumUnboxerMethodClassification classification =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java index 0d6fa85..f2fbb4c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -160,7 +160,8 @@ DexEncodedMethod singleTarget = appView .appInfo() - .resolveMethodOnClass(enumFieldType.getClassType(), factory.objectMembers.toString) + .resolveMethodOnClassLegacy( + enumFieldType.getClassType(), factory.objectMembers.toString) .getSingleTarget(); if (singleTarget != null && singleTarget.getReference() != factory.enumMembers.toString) { continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 8d2e339..c02b953 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -965,7 +965,7 @@ MethodResolutionResult resolutionResult = appView .appInfo() - .resolveMethodOnClass(clazz, appView.dexItemFactory().objectMembers.finalize); + .resolveMethodOnClassLegacy(clazz, appView.dexItemFactory().objectMembers.finalize); DexEncodedMethod target = resolutionResult.getSingleTarget(); return target != null && target.getReference() != dexItemFactory.enumMembers.finalize
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java index 9d4ae82..bd7edf7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -49,7 +49,7 @@ DexClass clazz = appInfo.definitionFor(classType); if (clazz != null && clazz.isEffectivelyFinal(appView)) { SingleResolutionResult<?> resolutionResult = - appInfo.resolveMethodOn(clazz, toStringMethodReference).asSingleResolution(); + appInfo.resolveMethodOnLegacy(clazz, toStringMethodReference).asSingleResolution(); if (resolutionResult != null && !resolutionResult.getResolvedMethod().getOptimizationInfo().mayHaveSideEffects()) { return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java index ef2f42d..d2ec4d6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -174,7 +174,10 @@ } SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOn(superClass, method.getReference()).asSingleResolution(); + appView + .appInfo() + .resolveMethodOnLegacy(superClass, method.getReference()) + .asSingleResolution(); if (resolutionResult == null) { return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java index 9402a62..afadf9f 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -576,11 +576,14 @@ public static class APIConverterConstructorCfCodeProvider extends SyntheticCfCodeProvider { - DexField wrapperField; + private final DexField wrapperField; + private final DexType superType; - public APIConverterConstructorCfCodeProvider(AppView<?> appView, DexField wrapperField) { + public APIConverterConstructorCfCodeProvider( + AppView<?> appView, DexField wrapperField, DexType superType) { super(appView, wrapperField.holder); this.wrapperField = wrapperField; + this.superType = superType; } @Override @@ -592,9 +595,7 @@ new CfInvoke( Opcodes.INVOKESPECIAL, factory.createMethod( - factory.objectType, - factory.createProto(factory.voidType), - factory.constructorMethodName), + superType, factory.createProto(factory.voidType), factory.constructorMethodName), false)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.type), 1));
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteArrayIterator.java b/src/main/java/com/android/tools/r8/lightir/ByteArrayIterator.java new file mode 100644 index 0000000..c1f72dc --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/ByteArrayIterator.java
@@ -0,0 +1,41 @@ +// 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.lightir; + +import it.unimi.dsi.fastutil.bytes.ByteIterator; + +/** Simple implementation of an iterator over a primitive byte array. */ +public class ByteArrayIterator implements ByteIterator { + + private final int size; + private final byte[] buffer; + private int index = 0; + + public ByteArrayIterator(byte[] bytes) { + size = bytes.length; + buffer = bytes; + } + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public byte nextByte() { + return buffer[index++]; + } + + @Override + public Byte next() { + return nextByte(); + } + + @Override + public int skip(int i) { + int actual = index + i <= size ? i : size - index; + index += actual; + return actual; + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteArrayWriter.java b/src/main/java/com/android/tools/r8/lightir/ByteArrayWriter.java new file mode 100644 index 0000000..7636ed4 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/ByteArrayWriter.java
@@ -0,0 +1,23 @@ +// 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.lightir; + +import java.io.ByteArrayOutputStream; + +/** Simple implementation to construct a primitive byte array. */ +public class ByteArrayWriter implements ByteWriter { + + // Backing is just the default capacity reallocating java byte array. + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + @Override + public void put(int u1) { + assert ByteUtils.isU1(u1); + buffer.write(u1); + } + + public byte[] toByteArray() { + return buffer.toByteArray(); + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java new file mode 100644 index 0000000..40a80d2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -0,0 +1,38 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.lightir; + +/** Simple utilities for byte encodings. */ +public class ByteUtils { + + public static boolean isU1(int value) { + return 0 <= value && value <= 0xFF; + } + + // Lossy truncation of an integer value to its lowest byte. + private static int truncateToU1(int value) { + return value & 0xFF; + } + + public static int ensureU1(int value) { + assert isU1(value); + return truncateToU1(value); + } + + public static int fromU1(byte value) { + return value & 0xFF; + } + + public static int intEncodingSize(int value) { + return 4; + } + + public static void writeEncodedInt(int value, ByteWriter writer) { + assert 4 == intEncodingSize(value); + writer.put(truncateToU1(value >> 24)); + writer.put(truncateToU1(value >> 16)); + writer.put(truncateToU1(value >> 8)); + writer.put(truncateToU1(value)); + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteWriter.java b/src/main/java/com/android/tools/r8/lightir/ByteWriter.java new file mode 100644 index 0000000..0a2a75e --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/ByteWriter.java
@@ -0,0 +1,11 @@ +// 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.lightir; + +/** Most primitive interface for providing consumer to the LIRWriter. */ +public interface ByteWriter { + + /** Put a byte value, must represent an unsigned byte (int between 0 and 255). */ + void put(int u1); +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java new file mode 100644 index 0000000..a08c036 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java
@@ -0,0 +1,18 @@ +// 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.lightir; + +public interface LIRBasicInstructionCallback { + + /** + * Most basic callback for interpreting LIR. + * + * @param opcode The opcode of the instruction (See {@code LIROpcodes} for values). + * @param operandsOffsetInBytes The offset into the byte stream at which the instruction's payload + * starts. + * @param operandsSizeInBytes The total size of the instruction's payload (excluding the opcode + * itself an any payload size encoding). + */ + void onInstruction(int opcode, int operandsOffsetInBytes, int operandsSizeInBytes); +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java new file mode 100644 index 0000000..7ced9ef --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -0,0 +1,52 @@ +// 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.lightir; + +import com.android.tools.r8.graph.DexItem; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; + +public class LIRBuilder { + + private final ByteArrayWriter byteWriter = new ByteArrayWriter(); + private final LIRWriter writer = new LIRWriter(byteWriter); + private final Reference2IntMap<DexItem> constants; + + public LIRBuilder() { + constants = new Reference2IntOpenHashMap<>(); + } + + private int getConstantIndex(DexItem item) { + int nextIndex = constants.size(); + Integer oldIndex = constants.putIfAbsent(item, nextIndex); + return oldIndex != null ? oldIndex : nextIndex; + } + + public LIRBuilder addNop() { + writer.writeOneByteInstruction(LIROpcodes.NOP); + return this; + } + + public LIRBuilder addConstNull() { + writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL); + return this; + } + + public LIRBuilder addConstInt(int value) { + if (0 <= value && value <= 5) { + writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value); + } else { + writer.writeInstruction(LIROpcodes.ICONST, ByteUtils.intEncodingSize(value)); + ByteUtils.writeEncodedInt(value, writer::writeOperand); + } + return this; + } + + public LIRCode build() { + int constantsCount = constants.size(); + DexItem[] constantTable = new DexItem[constantsCount]; + constants.forEach((item, index) -> constantTable[index] = item); + return new LIRCode(constantTable, byteWriter.toByteArray()); + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LIRCode.java new file mode 100644 index 0000000..c91dbd9 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -0,0 +1,27 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.lightir; + +import com.android.tools.r8.graph.DexItem; + +public class LIRCode implements Iterable<LIRInstructionView> { + + private final DexItem[] constants; + private final byte[] instructions; + + public static LIRBuilder builder() { + return new LIRBuilder(); + } + + // Should be constructed using LIRBuilder. + LIRCode(DexItem[] constants, byte[] instructions) { + this.constants = constants; + this.instructions = instructions; + } + + @Override + public LIRIterator iterator() { + return new LIRIterator(new ByteArrayIterator(instructions)); + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java new file mode 100644 index 0000000..59051e3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.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 com.android.tools.r8.lightir; + +/** + * Abstract view of a LIR instruction. + * + * <p>The view should not be considered a representation of an instruction as the underlying data + * can change. The view callbacks allow interpreting the instruction at different levels of + * abstraction depending on need. + */ +public interface LIRInstructionView { + + void accept(LIRBasicInstructionCallback eventCallback); +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java new file mode 100644 index 0000000..e05d521 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
@@ -0,0 +1,61 @@ +// 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.lightir; + +import it.unimi.dsi.fastutil.bytes.ByteIterator; +import java.util.Iterator; + +/** + * Basic iterator over the light IR. + * + * <p>This iterator is internally a zero-allocation parser with the "elements" as a view onto the + * current state. + */ +public class LIRIterator implements Iterator<LIRInstructionView>, LIRInstructionView { + + private final ByteIterator iterator; + + private int currentByteIndex = 0; + private int currentOpcode = -1; + private int currentOperandSize = 0; + + public LIRIterator(ByteIterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public LIRInstructionView next() { + currentOpcode = u1(); + if (LIROpcodes.isOneByteInstruction(currentOpcode)) { + currentOperandSize = 0; + } else { + // Any instruction that is not a single byte has a two-byte header. The second byte is the + // size of the variable width operand payload. + currentOperandSize = u1(); + skip(currentOperandSize); + } + return this; + } + + @Override + public void accept(LIRBasicInstructionCallback eventCallback) { + int operandsOffset = currentByteIndex - currentOperandSize; + eventCallback.onInstruction(currentOpcode, operandsOffset, currentOperandSize); + } + + private void skip(int i) { + currentByteIndex += i; + iterator.skip(i); + } + + private int u1() { + ++currentByteIndex; + return ByteUtils.fromU1(iterator.nextByte()); + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java new file mode 100644 index 0000000..85cc9f1 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -0,0 +1,182 @@ +// 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.lightir; + +/** + * Constants related to LIR. + * + * <p>The constants generally follow the bytecode values as defined by the classfile format. + */ +public interface LIROpcodes { + + static boolean isOneByteInstruction(int opcode) { + assert opcode >= NOP; + return opcode <= DCONST_1; + } + + // Instructions maintaining the same opcode as defined in CF. + int NOP = 0; + int ACONST_NULL = 1; + int ICONST_M1 = 2; + int ICONST_0 = 3; + int ICONST_1 = 4; + int ICONST_2 = 5; + int ICONST_3 = 6; + int ICONST_4 = 7; + int ICONST_5 = 8; + int LCONST_0 = 9; + int LCONST_1 = 10; + int FCONST_0 = 11; + int FCONST_1 = 12; + int FCONST_2 = 13; + int DCONST_0 = 14; + int DCONST_1 = 15; + // int BIPUSH = 16; + // int SIPUSH = 17; + int LDC = 18; + // int ILOAD = 21; + // int LLOAD = 22; + // int FLOAD = 23; + // int DLOAD = 24; + // int ALOAD = 25; + int IALOAD = 46; + int LALOAD = 47; + int FALOAD = 48; + int DALOAD = 49; + int AALOAD = 50; + int BALOAD = 51; + int CALOAD = 52; + int SALOAD = 53; + // int ISTORE = 54; + // int LSTORE = 55; + // int FSTORE = 56; + // int DSTORE = 57; + // int ASTORE = 58; + int IASTORE = 79; + int LASTORE = 80; + int FASTORE = 81; + int DASTORE = 82; + int AASTORE = 83; + int BASTORE = 84; + int CASTORE = 85; + int SASTORE = 86; + // int POP = 87; + // int POP2 = 88; + // int DUP = 89; + // int DUP_X1 = 90; + // int DUP_X2 = 91; + // int DUP2 = 92; + // int DUP2_X1 = 93; + // int DUP2_X2 = 94; + // int SWAP = 95; + int IADD = 96; + int LADD = 97; + int FADD = 98; + int DADD = 99; + int ISUB = 100; + int LSUB = 101; + int FSUB = 102; + int DSUB = 103; + int IMUL = 104; + int LMUL = 105; + int FMUL = 106; + int DMUL = 107; + int IDIV = 108; + int LDIV = 109; + int FDIV = 110; + int DDIV = 111; + int IREM = 112; + int LREM = 113; + int FREM = 114; + int DREM = 115; + int INEG = 116; + int LNEG = 117; + int FNEG = 118; + int DNEG = 119; + int ISHL = 120; + int LSHL = 121; + int ISHR = 122; + int LSHR = 123; + int IUSHR = 124; + int LUSHR = 125; + int IAND = 126; + int LAND = 127; + int IOR = 128; + int LOR = 129; + int IXOR = 130; + int LXOR = 131; + // int IINC = 132; + int I2L = 133; + int I2F = 134; + int I2D = 135; + int L2I = 136; + int L2F = 137; + int L2D = 138; + int F2I = 139; + int F2L = 140; + int F2D = 141; + int D2I = 142; + int D2L = 143; + int D2F = 144; + int I2B = 145; + int I2C = 146; + int I2S = 147; + int LCMP = 148; + int FCMPL = 149; + int FCMPG = 150; + int DCMPL = 151; + int DCMPG = 152; + int IFEQ = 153; + int IFNE = 154; + int IFLT = 155; + int IFGE = 156; + int IFGT = 157; + int IFLE = 158; + int IF_ICMPEQ = 159; + int IF_ICMPNE = 160; + int IF_ICMPLT = 161; + int IF_ICMPGE = 162; + int IF_ICMPGT = 163; + int IF_ICMPLE = 164; + int IF_ACMPEQ = 165; + int IF_ACMPNE = 166; + int GOTO = 167; + // int JSR = 168; + // int RET = 169; + int TABLESWITCH = 170; + int LOOKUPSWITCH = 171; + int IRETURN = 172; + int LRETURN = 173; + int FRETURN = 174; + int DRETURN = 175; + int ARETURN = 176; + int RETURN = 177; + int GETSTATIC = 178; + int PUTSTATIC = 179; + int GETFIELD = 180; + int PUTFIELD = 181; + int INVOKEVIRTUAL = 182; + int INVOKESPECIAL = 183; + int INVOKESTATIC = 184; + int INVOKEINTERFACE = 185; + int INVOKEDYNAMIC = 186; + int NEW = 187; + int NEWARRAY = 188; + int ANEWARRAY = 189; + int ARRAYLENGTH = 190; + int ATHROW = 191; + int CHECKCAST = 192; + int INSTANCEOF = 193; + int MONITORENTER = 194; + int MONITOREXIT = 195; + int MULTIANEWARRAY = 197; + int IFNULL = 198; + int IFNONNULL = 199; + + // Non-CF instructions. + int ICONST = 200; + int LCONST = 201; + int FCONST = 202; + int DCONST = 203; +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRWriter.java b/src/main/java/com/android/tools/r8/lightir/LIRWriter.java new file mode 100644 index 0000000..074726d --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LIRWriter.java
@@ -0,0 +1,39 @@ +// 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.lightir; + +/** + * Lowest level writer for constructing LIR encoded data. + * + * <p>This writer deals with just the instruction and operand encodings. For higher level structure, + * such as the constant pool, see LIRBuilder. + */ +public class LIRWriter { + + private final ByteWriter writer; + private int pendingOperandBytes = 0; + + public LIRWriter(ByteWriter writer) { + this.writer = writer; + } + + public void writeOneByteInstruction(int opcode) { + assert LIROpcodes.isOneByteInstruction(opcode); + assert pendingOperandBytes == 0; + writer.put(ByteUtils.ensureU1(opcode)); + } + + public void writeInstruction(int opcode, int operandsSizeInBytes) { + assert pendingOperandBytes == 0; + writer.put(ByteUtils.ensureU1(opcode)); + writer.put(ByteUtils.ensureU1(operandsSizeInBytes)); + pendingOperandBytes = operandsSizeInBytes; + } + + public void writeOperand(int u1) { + assert pendingOperandBytes > 0; + pendingOperandBytes--; + writer.put(ByteUtils.ensureU1(u1)); + } +}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java index 7657adc..0b0d20f 100644 --- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java +++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -47,16 +47,6 @@ private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>(); private LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>(); - private final Set<String> buildForClass; - - private Builder(Set<String> buildForClass) { - this.buildForClass = buildForClass; - } - - @Override - public boolean buildForClass(String typeName) { - return buildForClass == null || buildForClass.contains(typeName); - } @Override public ClassNamingForNameMapper.Builder classNamingBuilder( @@ -88,11 +78,7 @@ } public static Builder builder() { - return new Builder(null); - } - - public static Builder builder(Set<String> buildForClass) { - return new Builder(buildForClass); + return new Builder(); } public static ClassNameMapper mapperFromFile(Path path) throws IOException { @@ -182,7 +168,7 @@ diagnosticsHandler != null ? diagnosticsHandler : new Reporter(), allowEmptyMappedRanges, allowExperimentalMapping)) { - ClassNameMapper.Builder builder = ClassNameMapper.builder(buildForClass); + ClassNameMapper.Builder builder = ClassNameMapper.builder(); proguardReader.parse(builder); return builder.build(); }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java index ca7fa4e..242b6e1 100644 --- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -382,7 +382,8 @@ return; } - MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOn(holder, method); + MethodResolutionResult resolutionResult = + appView.appInfo().resolveMethodOnLegacy(holder, method); if (resolutionResult.isSingleResolution()) { DexEncodedMethod resolvedMethod = resolutionResult.getSingleTarget(); if (resolvedMethod.getReference() == method) {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java index bf18111..ddd882c 100644 --- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java +++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -106,7 +106,8 @@ return true; } - MethodResolutionResult resolution = appView.appInfo().unsafeResolveMethodDueToDexFormat(method); + MethodResolutionResult resolution = + appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method); assert resolution != null; if (resolution.isSingleResolution()) {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java index d1e7463..6b95444 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMap.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -16,10 +16,6 @@ abstract Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion); abstract ProguardMap build(); - - public boolean buildForClass(String typeName) { - return true; - } } boolean hasMapping(DexType type);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java index bbea4c4..f28d4bc 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -261,18 +261,11 @@ String after = parseType(false); skipWhitespace(); expect(':'); - if (mapBuilder.buildForClass(after)) { - ClassNaming.Builder currentClassBuilder = - mapBuilder.classNamingBuilder(after, before, getPosition()); - skipWhitespace(); - if (nextLine()) { - parseMemberMappings(currentClassBuilder); - } - } else { - do { - lineOffset = line.length(); - nextLine(); - } while (hasLine() && !isClassMapping()); + ClassNaming.Builder currentClassBuilder = + mapBuilder.classNamingBuilder(after, before, getPosition()); + skipWhitespace(); + if (nextLine()) { + parseMemberMappings(currentClassBuilder); } } }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 2e373e0..a03ff91 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -116,7 +116,7 @@ currentResolutionResult = appView .appInfo() - .resolveMethodOnClass(currentResolvedHolder.getSuperType(), original) + .resolveMethodOnClassLegacy(currentResolvedHolder.getSuperType(), original) .asSingleResolution(); } else { break; @@ -244,15 +244,15 @@ } private MethodResolutionResult resolveMethodOnClass(DexMethod method) { - return appView.appInfo().resolveMethodOnClass(method.holder, method); + return appView.appInfo().resolveMethodOnClassLegacy(method.holder, method); } private MethodResolutionResult resolveMethodOnInterface(DexMethod method) { - return appView.appInfo().resolveMethodOnInterface(method.holder, method); + return appView.appInfo().resolveMethodOnInterfaceLegacy(method.holder, method); } private MethodResolutionResult resolveMethod(DexMethod method) { - return appView.appInfo().unsafeResolveMethodDueToDexFormat(method); + return appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method); } private void computeMethodRebinding(MethodAccessInfoCollection methodAccessInfoCollection) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java index b819518..6782981 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -216,7 +216,7 @@ DexClass holder = appView.contextIndependentDefinitionFor(reference.getHolderType()); if (holder != null) { SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution(); + appView.appInfo().resolveMethodOnLegacy(holder, reference).asSingleResolution(); if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) { recordNonReboundMethodAccess( reference, resolutionResult.getResolvedMethod().getReference());
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java index 38c08c6..6ae7365 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -216,7 +216,7 @@ return; } SingleResolutionResult<?> resolutionResult = - appInfo.resolveMethodOn(holder, method).asSingleResolution(); + appInfo.resolveMethodOnLegacy(holder, method).asSingleResolution(); if (resolutionResult == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java index cbb7761..1b72c3a 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
@@ -22,7 +22,7 @@ return false; } SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOn(clazz, method).asSingleResolution(); + appView.appInfo().resolveMethodOnLegacy(clazz, method).asSingleResolution(); return resolutionResult != null && resolutionResult.getResolvedHolder().getType() != method.getHolderType(); }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java index 8055dc4..16e3dfb 100644 --- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java +++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -53,7 +53,10 @@ } // This is a visibility forward, so check for the direct target. ProgramMethod targetMethod = - appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getResolvedProgramMethod(); + appView + .appInfo() + .unsafeResolveMethodDueToDexFormatLegacy(target) + .getResolvedProgramMethod(); if (targetMethod == null || !targetMethod.getAccessFlags().isPublic()) { return false; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java index 150f7ba..d4c83b0 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -164,7 +164,10 @@ } SingleResolutionResult<?> resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution(); + appView + .appInfo() + .unsafeResolveMethodDueToDexFormatLegacy(invokedMethod) + .asSingleResolution(); if (resolutionResult == null) { // Nothing to propagate; the invoke instruction fails. return; @@ -543,7 +546,7 @@ SingleResolutionResult<?> resolution = appView .appInfo() - .resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface) + .resolveMethodLegacy(bootstrapMethod.asMethod(), bootstrapMethod.isInterface) .asSingleResolution(); if (resolution != null && resolution.getResolvedHolder().isProgramClass()) { methodStates.set(resolution.getResolvedProgramMethod(), UnknownMethodState.get());
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java index f7f7d06..cd962c3 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -188,7 +188,7 @@ private void registerInvokeMethod(DexMethod method) { SingleResolutionResult<?> resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(method).asSingleResolution(); + appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method).asSingleResolution(); if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) { return; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java index b41cdda..675bff8 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -131,7 +131,7 @@ interfaceState.forEach( (interfaceMethod, interfaceMethodState) -> { MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnClass(subclass, interfaceMethod); + appView.appInfo().resolveMethodOnClassLegacy(subclass, interfaceMethod); if (resolutionResult.isFailedResolution()) { // TODO(b/190154391): Do we need to propagate argument information to the first // virtual method above the inaccessible method in the class hierarchy?
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java index 59d8ba7..a1aec5a 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -60,9 +60,6 @@ // memory usage, but would require visiting all transitive (program) super classes for each // subclass. private void addParentState(DexProgramClass clazz, DexProgramClass superclass) { - ClassTypeElement classType = - TypeElement.fromDexType(clazz.getType(), maybeNull(), appView).asClassType(); - PropagationState parentState = propagationStates.get(superclass.asProgramClass()); assert parentState != null; @@ -87,7 +84,7 @@ parentState.inactiveUntilUpperBound.forEach( (bounds, inactiveMethodStates) -> { ClassTypeElement upperBound = bounds.getDynamicUpperBoundType().asClassType(); - if (upperBound.equalUpToNullability(classType)) { + if (shouldActivateMethodStateGuardedByBounds(upperBound, clazz, superclass)) { // The upper bound is the current class, thus this inactive information now becomes // active. if (bounds.hasDynamicLowerBoundType()) { @@ -103,7 +100,10 @@ inactiveMethodStates.forEach( (signature, methodState) -> { SingleResolutionResult<?> resolutionResult = - appView.appInfo().resolveMethodOn(clazz, signature).asSingleResolution(); + appView + .appInfo() + .resolveMethodOnLegacy(clazz, signature) + .asSingleResolution(); // Find the first virtual method in the super class hierarchy. while (resolutionResult != null @@ -111,7 +111,7 @@ resolutionResult = appView .appInfo() - .resolveMethodOnClass( + .resolveMethodOnClassLegacy( resolutionResult.getResolvedHolder().getSuperType(), signature) .asSingleResolution(); } @@ -158,6 +158,20 @@ } return methodState; } + + private boolean shouldActivateMethodStateGuardedByBounds( + ClassTypeElement upperBound, DexProgramClass currentClass, DexProgramClass superClass) { + ClassTypeElement classType = + TypeElement.fromDexType(currentClass.getType(), maybeNull(), appView).asClassType(); + // When propagating argument information for interface methods downwards from an interface to + // a non-interface we need to account for the parent classes of the current class. + if (superClass.isInterface() + && !currentClass.isInterface() + && currentClass.getSuperType() != appView.dexItemFactory().objectType) { + return classType.lessThanOrEqualUpToNullability(upperBound, appView); + } + return classType.equalUpToNullability(upperBound); + } } // For each class, stores the argument information for each virtual method on this class and all @@ -224,7 +238,7 @@ // TODO(b/190154391): Verify that the bounds are not trivial according to the // static receiver type. ClassTypeElement upperBound = bounds.getDynamicUpperBoundType().asClassType(); - if (isUpperBoundSatisfied(upperBound, clazz, propagationState)) { + if (isUpperBoundSatisfied(upperBound, clazz)) { if (bounds.hasDynamicLowerBoundType()) { // TODO(b/190154391): Verify that the lower bound is a subtype of the current // class. @@ -255,10 +269,7 @@ propagationStates.put(clazz, propagationState); } - private boolean isUpperBoundSatisfied( - ClassTypeElement upperBound, - DexProgramClass currentClass, - PropagationState propagationState) { + private boolean isUpperBoundSatisfied(ClassTypeElement upperBound, DexProgramClass currentClass) { DexType upperBoundType = upperBound.getClassType() == appView.dexItemFactory().objectType && upperBound.getInterfaces().hasSingleKnownInterface()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java index 87f26e7..f872fd7 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -127,7 +127,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .unsafeResolveMethodDueToDexFormatLegacy(invoke.getInvokedMethod()) .asSingleResolution(); if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java index b0be28f..2bc10c5 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -150,7 +150,7 @@ ProgramMethod resolvedMethod = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .unsafeResolveMethodDueToDexFormatLegacy(invoke.getInvokedMethod()) .getResolvedProgramMethod(); if (resolvedMethod == null || isUnoptimizable(resolvedMethod)) { return null;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java index 14d47ac..cc81166 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java
@@ -84,7 +84,11 @@ } public boolean isRoot(DexProgramClass clazz) { - DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(clazz.getSuperType())); + DexType superType = clazz.getSuperType(); + if (superType == null) { + return true; + } + DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(superType)); if (superclass != null) { return false; }
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java index 1bd92a3..f640d08 100644 --- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java +++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -166,7 +166,7 @@ DexEncodedMethod definition = subclass.lookupVirtualMethod(method); if (definition == null) { DexEncodedMethod resolutionTarget = - appView.appInfo().resolveMethodOnClass(subclass, method).getSingleTarget(); + appView.appInfo().resolveMethodOnClassLegacy(subclass, method).getSingleTarget(); if (resolutionTarget == null || resolutionTarget.isAbstract()) { // The fact that this class does not declare the bridge (or the bridge is abstract) should // not prevent us from hoisting the bridge. @@ -228,7 +228,7 @@ // The targeted method must be present on the new holder class for this to be feasible. MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnClass(clazz, methodToInvoke); + appView.appInfo().resolveMethodOnClassLegacy(clazz, methodToInvoke); if (!resolutionResult.isSingleResolution()) { return; }
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java new file mode 100644 index 0000000..7572341 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/BottomCfFrameState.java
@@ -0,0 +1,90 @@ +// 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.optimize.interfaces.analysis; + +import com.android.tools.r8.cf.code.CfFrame.FrameType; +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.ProgramMethod; +import java.util.function.Function; + +public class BottomCfFrameState extends CfFrameState { + + private static final BottomCfFrameState INSTANCE = new BottomCfFrameState(); + + private BottomCfFrameState() {} + + static BottomCfFrameState getInstance() { + return INSTANCE; + } + + @Override + public CfFrameState markInitialized(FrameType uninitializedType, DexType initializedType) { + return error(); + } + + @Override + public CfFrameState pop() { + return error(); + } + + @Override + public CfFrameState pop(Function<FrameType, CfFrameState> fn) { + return error(); + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType expectedType) { + return error(); + } + + @Override + public CfFrameState pop( + AppView<?> appView, FrameType expectedType, Function<FrameType, CfFrameState> fn) { + return error(); + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType... expectedTypes) { + return error(); + } + + @Override + public CfFrameState popAndInitialize( + AppView<?> appView, DexMethod constructor, ProgramMethod context) { + return error(); + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType expectedType) { + return error(); + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) { + return error(); + } + + @Override + public CfFrameState push(DexType type) { + return new ConcreteCfFrameState().push(type); + } + + @Override + public CfFrameState push(FrameType frameType) { + return new ConcreteCfFrameState().push(frameType); + } + + @Override + public boolean equals(Object other) { + return this == other; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java new file mode 100644 index 0000000..ec899a2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
@@ -0,0 +1,67 @@ +// 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.optimize.interfaces.analysis; + +import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.errors.Unimplemented; +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.ProgramMethod; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState; +import java.util.function.Function; + +public abstract class CfFrameState extends AbstractState<CfFrameState> { + + public static CfFrameState bottom() { + return BottomCfFrameState.getInstance(); + } + + public static ErroneousCfFrameState error() { + return ErroneousCfFrameState.getInstance(); + } + + @Override + public CfFrameState asAbstractState() { + return this; + } + + public abstract CfFrameState markInitialized( + FrameType uninitializedType, DexType initializedType); + + public abstract CfFrameState pop(); + + public abstract CfFrameState pop(Function<FrameType, CfFrameState> fn); + + public abstract CfFrameState pop(AppView<?> appView, FrameType expectedType); + + public abstract CfFrameState pop( + AppView<?> appView, FrameType expectedType, Function<FrameType, CfFrameState> fn); + + public abstract CfFrameState pop(AppView<?> appView, FrameType... expectedTypes); + + public abstract CfFrameState popAndInitialize( + AppView<?> appView, DexMethod constructor, ProgramMethod context); + + public abstract CfFrameState popInitialized(AppView<?> appView, DexType expectedType); + + public abstract CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes); + + public abstract CfFrameState push(DexType type); + + public abstract CfFrameState push(FrameType frameType); + + @Override + public final CfFrameState join(CfFrameState state) { + // TODO(b/214496607): Implement join. + throw new Unimplemented(); + } + + @Override + public abstract boolean equals(Object other); + + @Override + public abstract int hashCode(); +}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java new file mode 100644 index 0000000..feed0d2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -0,0 +1,76 @@ +// 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.optimize.interfaces.analysis; + +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.errors.Unimplemented; +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.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfBlock; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfControlFlowGraph; +import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfIntraproceduralDataflowAnalysis; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class CfOpenClosedInterfacesAnalysis { + + private final AppView<AppInfoWithLiveness> appView; + + public CfOpenClosedInterfacesAnalysis(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + public void run() { + // TODO(b/214496607): Parallelize the analysis. + for (DexProgramClass clazz : appView.appInfo().classes()) { + clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod); + } + } + + private void processMethod(ProgramMethod method) { + Code code = method.getDefinition().getCode(); + if (!code.isCfCode()) { + assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode(); + return; + } + + CfCode cfCode = code.asCfCode(); + CfControlFlowGraph cfg = CfControlFlowGraph.create(cfCode); + CfIntraproceduralDataflowAnalysis<CfFrameState> analysis = + new CfIntraproceduralDataflowAnalysis<>( + CfFrameState.bottom(), cfg, new TransferFunction(method)); + DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock()); + // TODO(b/214496607): Determine open interfaces. + } + + private class TransferFunction + implements AbstractTransferFunction<CfBlock, CfInstruction, CfFrameState> { + + private final ProgramMethod context; + + TransferFunction(ProgramMethod context) { + this.context = context; + } + + @Override + public TransferFunctionResult<CfFrameState> apply( + CfInstruction instruction, CfFrameState state) { + return instruction.evaluate(state, context, appView, appView.dexItemFactory()); + } + + @Override + public CfFrameState computeBlockEntryState( + CfBlock cfBlock, CfBlock predecessor, CfFrameState predecessorExitState) { + // TODO(b/214496607): Implement this. + throw new Unimplemented(); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java new file mode 100644 index 0000000..63654b3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
@@ -0,0 +1,169 @@ +// 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.optimize.interfaces.analysis; + +import static com.android.tools.r8.cf.code.CfFrame.getInitializedFrameType; + +import com.android.tools.r8.cf.code.CfAssignability; +import com.android.tools.r8.cf.code.CfFrame.FrameType; +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.ProgramMethod; +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Objects; +import java.util.function.Function; + +public class ConcreteCfFrameState extends CfFrameState { + + private final Int2ObjectSortedMap<FrameType> locals; + private final Deque<FrameType> stack; + + ConcreteCfFrameState() { + this(new Int2ObjectAVLTreeMap<>(), new ArrayDeque<>()); + } + + ConcreteCfFrameState(Int2ObjectSortedMap<FrameType> locals, Deque<FrameType> stack) { + this.locals = locals; + this.stack = stack; + } + + @Override + public CfFrameState markInitialized(FrameType uninitializedType, DexType initializedType) { + if (uninitializedType.isInitialized()) { + return error(); + } + for (Int2ObjectMap.Entry<FrameType> entry : locals.int2ObjectEntrySet()) { + FrameType frameType = entry.getValue(); + FrameType initializedFrameType = + getInitializedFrameType(uninitializedType, frameType, initializedType); + entry.setValue(initializedFrameType); + } + // TODO(b/214496607): By using a collection that supports element replacement this could mutate + // the existing stack instead of building a new one. + Deque<FrameType> newStack = new ArrayDeque<>(); + for (FrameType frameType : stack) { + FrameType initializedFrameType = + getInitializedFrameType(uninitializedType, frameType, initializedType); + newStack.addLast(initializedFrameType); + } + return new ConcreteCfFrameState(locals, newStack); + } + + @Override + public CfFrameState pop() { + if (stack.isEmpty()) { + return error(); + } + stack.removeLast(); + return this; + } + + @Override + public CfFrameState pop(Function<FrameType, CfFrameState> fn) { + if (stack.isEmpty()) { + return error(); + } + FrameType frameType = stack.removeLast(); + return fn.apply(frameType); + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType expectedType) { + return pop(appView, expectedType, ignore -> this); + } + + @Override + public CfFrameState pop( + AppView<?> appView, FrameType expectedType, Function<FrameType, CfFrameState> fn) { + return pop( + frameType -> + CfAssignability.isAssignable(frameType, expectedType, appView) + ? fn.apply(frameType) + : error()); + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType... expectedTypes) { + CfFrameState state = this; + for (int i = expectedTypes.length - 1; i >= 0; i--) { + state = state.pop(appView, expectedTypes[i]); + } + return state; + } + + @Override + public CfFrameState popAndInitialize( + AppView<?> appView, DexMethod constructor, ProgramMethod context) { + return pop( + frameType -> { + if (frameType.isUninitializedThis()) { + if (constructor.getHolderType() == context.getHolderType() + || constructor.getHolderType() == context.getHolder().getSuperType()) { + return markInitialized(frameType, context.getHolderType()); + } + } else if (frameType.isUninitializedNew()) { + DexType uninitializedNewType = frameType.getUninitializedNewType(); + if (constructor.getHolderType() == uninitializedNewType) { + return markInitialized(frameType, uninitializedNewType); + } + } + return error(); + }); + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType expectedType) { + return pop( + frameType -> + frameType.isInitialized() + && CfAssignability.isAssignable( + frameType.getInitializedType(), expectedType, appView) + ? this + : error()); + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) { + CfFrameState state = this; + for (int i = expectedTypes.length - 1; i >= 0; i--) { + state = state.popInitialized(appView, expectedTypes[i]); + } + return state; + } + + @Override + public CfFrameState push(DexType type) { + return push(FrameType.initialized(type)); + } + + @Override + public CfFrameState push(FrameType frameType) { + stack.push(frameType); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + // TODO(b/214496607): FrameType should implement equals() and hashCode(). + ConcreteCfFrameState that = (ConcreteCfFrameState) o; + return locals.equals(that.locals) && stack.equals(that.stack); + } + + @Override + public int hashCode() { + return Objects.hash(locals, stack); + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java new file mode 100644 index 0000000..7e71f93 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ErroneousCfFrameState.java
@@ -0,0 +1,91 @@ +// 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.optimize.interfaces.analysis; + +import com.android.tools.r8.cf.code.CfFrame.FrameType; +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.ProgramMethod; +import java.util.function.Function; + +/** An analysis state representing that the code does not type check. */ +public class ErroneousCfFrameState extends CfFrameState { + + private static final ErroneousCfFrameState INSTANCE = new ErroneousCfFrameState(); + + private ErroneousCfFrameState() {} + + static ErroneousCfFrameState getInstance() { + return INSTANCE; + } + + @Override + public CfFrameState markInitialized(FrameType uninitializedType, DexType initializedType) { + return this; + } + + @Override + public CfFrameState pop() { + return this; + } + + @Override + public CfFrameState pop(Function<FrameType, CfFrameState> fn) { + return this; + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType expectedType) { + return this; + } + + @Override + public CfFrameState pop( + AppView<?> appView, FrameType expectedType, Function<FrameType, CfFrameState> fn) { + return this; + } + + @Override + public CfFrameState pop(AppView<?> appView, FrameType... expectedTypes) { + return this; + } + + @Override + public CfFrameState popAndInitialize( + AppView<?> appView, DexMethod constructor, ProgramMethod context) { + return this; + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType expectedType) { + return this; + } + + @Override + public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) { + return this; + } + + @Override + public CfFrameState push(DexType type) { + return this; + } + + @Override + public CfFrameState push(FrameType frameType) { + return this; + } + + @Override + public boolean equals(Object other) { + return this == other; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } +}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java index e17be11..63f4483 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -160,7 +160,7 @@ appView.definitionFor(method.getHolder().getSuperType(), method.getHolder()); if (superClass != null) { registry.registerMemberAccess( - appView.appInfo().resolveMethodOn(superClass, method.getReference())); + appView.appInfo().resolveMethodOnLegacy(superClass, method.getReference())); } // Trace the references in the method and method parameter annotations.
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java index 1339cbf..e5d00a9 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -108,7 +108,8 @@ } public ProgramMethod registerMethodReference(DexMethod method) { - MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method); + MethodResolutionResult resolutionResult = + appInfo.unsafeResolveMethodDueToDexFormatLegacy(method); registerMemberAccess(resolutionResult, false); return resolutionResult.isSingleResolution() ? resolutionResult.asSingleResolution().getResolvedProgramMethod() @@ -209,7 +210,7 @@ @Override public void registerInvokeVirtual(DexMethod invokedMethod) { registerMemberAccessForInvoke( - appInfo.resolveMethod( + appInfo.resolveMethodLegacy( graphLens.lookupInvokeVirtual(invokedMethod, methodContext, codeLens).getReference(), false)); } @@ -217,21 +218,21 @@ @Override public void registerInvokeDirect(DexMethod invokedMethod) { registerMemberAccessForInvoke( - appInfo.unsafeResolveMethodDueToDexFormat( + appInfo.unsafeResolveMethodDueToDexFormatLegacy( graphLens.lookupInvokeDirect(invokedMethod, methodContext, codeLens).getReference())); } @Override public void registerInvokeStatic(DexMethod invokedMethod) { registerMemberAccessForInvoke( - appInfo.unsafeResolveMethodDueToDexFormat( + appInfo.unsafeResolveMethodDueToDexFormatLegacy( graphLens.lookupInvokeStatic(invokedMethod, methodContext, codeLens).getReference())); } @Override public void registerInvokeInterface(DexMethod invokedMethod) { registerMemberAccessForInvoke( - appInfo.resolveMethod( + appInfo.resolveMethodLegacy( graphLens.lookupInvokeInterface(invokedMethod, methodContext, codeLens).getReference(), true)); } @@ -239,7 +240,7 @@ @Override public void registerInvokeSuper(DexMethod invokedMethod) { registerMemberAccessForInvoke( - appInfo.unsafeResolveMethodDueToDexFormat( + appInfo.unsafeResolveMethodDueToDexFormatLegacy( graphLens.lookupInvokeSuper(invokedMethod, methodContext, codeLens).getReference())); }
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java index 82a04e6..890e4d3 100644 --- a/src/main/java/com/android/tools/r8/retrace/Retrace.java +++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -222,7 +222,7 @@ resultsForLine.add(Pair.create(retracedElement, currentList.get())); contexts.add(retracedElement.getContext()); } else { - currentList.empty(); + currentList.clear(); } } if (currentList.isSet()) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java index 279a038..d42da83 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
@@ -4,8 +4,11 @@ package com.android.tools.r8.retrace.internal; +import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.COMPLETE_CLASS_MAPPING; +import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.IS_COMMENT_SOURCE_FILE; import static java.lang.Integer.MAX_VALUE; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.naming.LineReader; import java.io.IOException; import java.io.InputStream; @@ -15,12 +18,182 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Set; public abstract class ProguardMapReaderWithFiltering implements LineReader { + // The LineParserState encodes a simple state that the line parser can be in, where the + // (successful) transitions allowed are: + + // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START + + // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE + + // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING + // + // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE. + // The terminal states are: + // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE } + // + public enum LineParserState { + BEGINNING, + BEGINNING_NO_WHITESPACE, + SEEN_ORIGINAL_CLASS, + SEEN_ARROW, + SEEN_OBFUSCATED_CLASS, + COMPLETE_CLASS_MAPPING, + IS_COMMENT_START, + IS_COMMENT_SOURCE_FILE, + NOT_CLASS_MAPPING_OR_SOURCE_FILE; + + private boolean isTerminal() { + return this == NOT_CLASS_MAPPING_OR_SOURCE_FILE + || this == COMPLETE_CLASS_MAPPING + || this == IS_COMMENT_SOURCE_FILE; + } + + private static int currentIndex; + private static int endIndex; + private static byte[] bytes; + private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes(); + + public static LineParserState computeState(byte[] bytes, int startIndex, int endIndex) { + currentIndex = startIndex; + LineParserState.endIndex = endIndex; + LineParserState.bytes = bytes; + LineParserState currentState = BEGINNING; + while (!currentState.isTerminal()) { + currentState = currentState.computeNextState(); + } + return currentState; + } + + private LineParserState computeNextState() { + assert this != NOT_CLASS_MAPPING_OR_SOURCE_FILE; + switch (this) { + case BEGINNING: + return readUntilNoWhiteSpace() + ? BEGINNING_NO_WHITESPACE + : NOT_CLASS_MAPPING_OR_SOURCE_FILE; + case BEGINNING_NO_WHITESPACE: + if (isCommentChar()) { + return IS_COMMENT_START; + } else { + int readLength = readCharactersNoWhiteSpaceUntil(' '); + return readLength > 0 ? SEEN_ORIGINAL_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE; + } + case SEEN_ORIGINAL_CLASS: + return readArrow() ? SEEN_ARROW : NOT_CLASS_MAPPING_OR_SOURCE_FILE; + case SEEN_ARROW: + int colonIndex = readCharactersNoWhiteSpaceUntil(':'); + return colonIndex > 0 ? SEEN_OBFUSCATED_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE; + case SEEN_OBFUSCATED_CLASS: + boolean read = readColon(); + if (!read) { + return NOT_CLASS_MAPPING_OR_SOURCE_FILE; + } + boolean noWhiteSpace = readUntilNoWhiteSpace(); + return (!noWhiteSpace || isCommentChar()) + ? COMPLETE_CLASS_MAPPING + : NOT_CLASS_MAPPING_OR_SOURCE_FILE; + case IS_COMMENT_START: + if (readCharactersUntil('{') + && readCharactersUntil(':') + && readSingleOrDoubleQuote() + && readSourceFile()) { + return IS_COMMENT_SOURCE_FILE; + } else { + return NOT_CLASS_MAPPING_OR_SOURCE_FILE; + } + default: + assert isTerminal(); + throw new Unreachable("Should not compute next state on terminal state"); + } + } + + private boolean readColon() { + return read(':'); + } + + private boolean readCharactersUntil(char ch) { + while (currentIndex < endIndex) { + if (bytes[currentIndex++] == ch) { + return true; + } + } + return false; + } + + private int readCharactersNoWhiteSpaceUntil(char ch) { + int startIndex = currentIndex; + while (currentIndex < endIndex) { + byte readByte = bytes[currentIndex]; + if (readByte == ch) { + return currentIndex - startIndex; + } + if (Character.isWhitespace(readByte)) { + return -1; + } + currentIndex++; + } + return -1; + } + + private boolean readUntilNoWhiteSpace() { + while (currentIndex < endIndex) { + if (!Character.isWhitespace(bytes[currentIndex])) { + return true; + } + currentIndex++; + } + return false; + } + + private boolean readArrow() { + return readSpace() && read('-') && read('>') && readSpace(); + } + + private boolean readSpace() { + return read(' '); + } + + private boolean read(char ch) { + return bytes[currentIndex++] == ch; + } + + private boolean isCommentChar() { + return bytes[currentIndex] == '#'; + } + + private boolean readSourceFile() { + if (endIndex - currentIndex < SOURCE_FILE_BYTES.length) { + return false; + } + int endSourceFileIndex = currentIndex + SOURCE_FILE_BYTES.length; + int sourceFileByteIndex = 0; + for (; currentIndex < endSourceFileIndex; currentIndex++) { + if (SOURCE_FILE_BYTES[sourceFileByteIndex++] != bytes[currentIndex]) { + return false; + } + } + return readSingleOrDoubleQuote(); + } + + private boolean readSingleOrDoubleQuote() { + byte readByte = bytes[currentIndex++]; + return readByte == '\'' || readByte == '"'; + } + } + private int startIndex = 0; private int endIndex = 0; + private final Set<String> filter; + + protected ProguardMapReaderWithFiltering(Set<String> filter) { + this.filter = filter; + } + public abstract byte[] read() throws IOException; public abstract int getStartIndex(); @@ -29,15 +202,43 @@ public abstract boolean exceedsBuffer(); + private boolean isInsideClassOfInterest = false; + private boolean seenFirstClass = false; + @Override public String readLine() throws IOException { - byte[] bytes = readLineFromMultipleReads(); - if (bytes == null) { - return null; + while (true) { + byte[] bytes = readLineFromMultipleReads(); + if (bytes == null) { + return null; + } + if (filter == null) { + return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8); + } + LineParserState lineParserState = LineParserState.computeState(bytes, startIndex, endIndex); + if (lineParserState == COMPLETE_CLASS_MAPPING) { + seenFirstClass = true; + String classMapping = getBufferAsString(bytes); + String obfuscatedClassName = getObfuscatedClassName(classMapping); + isInsideClassOfInterest = filter.contains(obfuscatedClassName); + return classMapping; + } else if (lineParserState == IS_COMMENT_SOURCE_FILE) { + return getBufferAsString(bytes); + } else if (isInsideClassOfInterest || !seenFirstClass) { + return getBufferAsString(bytes); + } } + } + + private String getBufferAsString(byte[] bytes) { return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8); } + private String getObfuscatedClassName(String classMapping) { + int arrowIndex = classMapping.indexOf(">"); + return classMapping.substring(arrowIndex + 2, classMapping.length() - 1); + } + private byte[] readLineFromMultipleReads() throws IOException { startIndex = 0; endIndex = 0; @@ -82,7 +283,9 @@ private int currentPosition = 0; private int temporaryBufferPosition = 0; - public ProguardMapReaderWithFilteringMappedBuffer(Path mappingFile) throws IOException { + public ProguardMapReaderWithFilteringMappedBuffer( + Path mappingFile, Set<String> classNamesOfInterest) throws IOException { + super(classNamesOfInterest); fileChannel = FileChannel.open(mappingFile, StandardOpenOption.READ); channelSize = fileChannel.size(); readFromChannel(); @@ -160,7 +363,9 @@ private int endIndex = 0; private int endReadIndex = 0; - public ProguardMapReaderWithFilteringInputBuffer(InputStream inputStream) { + public ProguardMapReaderWithFilteringInputBuffer( + InputStream inputStream, Set<String> classNamesOfInterest) { + super(classNamesOfInterest); this.inputStream = inputStream; }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java index cb6991e..a5c1dd6 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
@@ -64,17 +64,16 @@ @Override public ProguardMappingProvider build() { try { + Set<String> buildForClass = allowLookupAllClasses ? null : allowedLookup; LineReader reader = proguardMapProducer.isFileBacked() - ? new ProguardMapReaderWithFilteringMappedBuffer(proguardMapProducer.getPath()) - : new ProguardMapReaderWithFilteringInputBuffer(proguardMapProducer.get()); + ? new ProguardMapReaderWithFilteringMappedBuffer( + proguardMapProducer.getPath(), buildForClass) + : new ProguardMapReaderWithFilteringInputBuffer( + proguardMapProducer.get(), buildForClass); return new ProguardMappingProviderImpl( ClassNameMapper.mapperFromLineReaderWithFiltering( - reader, - diagnosticsHandler, - true, - allowExperimental, - allowLookupAllClasses ? null : allowedLookup)); + reader, diagnosticsHandler, true, allowExperimental, buildForClass)); } catch (Exception e) { throw new InvalidMappingFileException(e); }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index 34425b3..5a6ef63 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1388,7 +1388,7 @@ return cachedItem; } SingleResolutionResult<?> resolution = - resolveMethodOn(initialResolutionHolder, method).asSingleResolution(); + resolveMethodOnLegacy(initialResolutionHolder, method).asSingleResolution(); if (resolution == null || resolution.isAccessibleForVirtualDispatchFrom(context.getHolder(), this).isFalse()) { return null;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index 98562f9..47baa0b 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2299,7 +2299,8 @@ private SingleResolutionResult<?> resolveMethod( DexMethod method, ProgramDefinition context, KeepReason reason) { // Record the references in case they are not program types. - MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method); + MethodResolutionResult resolutionResult = + appInfo.unsafeResolveMethodDueToDexFormatLegacy(method); if (resolutionResult.isFailedResolution()) { markFailedMethodResolutionTargets( method, resolutionResult.asFailedResolution(), context, reason); @@ -2313,7 +2314,7 @@ private SingleResolutionResult<?> resolveMethod( DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) { // Record the references in case they are not program types. - MethodResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke); + MethodResolutionResult resolutionResult = appInfo.resolveMethodLegacy(method, interfaceInvoke); if (resolutionResult.isSingleResolution()) { recordMethodReference( method, resolutionResult.getResolutionPair().asProgramDerivedContext(context)); @@ -2794,7 +2795,8 @@ (resolutionSearchKey, contexts) -> { SingleResolutionResult<?> singleResolution = appInfo - .resolveMethod(resolutionSearchKey.method, resolutionSearchKey.isInterface) + .resolveMethodLegacy( + resolutionSearchKey.method, resolutionSearchKey.isInterface) .asSingleResolution(); if (singleResolution == null) { assert false : "Should not be null"; @@ -2859,7 +2861,7 @@ markLibraryOrClasspathOverrideLive( instantiation, libraryClass, - appInfo.resolveMethodOn(libraryClass, method.getReference())); + appInfo.resolveMethodOnLegacy(libraryClass, method.getReference())); // Due to API conversion, some overrides can be hidden since they will be rewritten. See // class comment of DesugaredLibraryAPIConverter and vivifiedType logic. @@ -2876,7 +2878,7 @@ markLibraryOrClasspathOverrideLive( instantiation, libraryClass, - appInfo.resolveMethodOn(instantiation.asClass(), methodToResolve)); + appInfo.resolveMethodOnLegacy(instantiation.asClass(), methodToResolve)); } } } @@ -2941,6 +2943,9 @@ markFieldAsLive(field, clazz, reason); } } + if (clazz.superType == null) { + break; + } clazz = getProgramClassOrNull(clazz.superType, clazz); } while (clazz != null && !objectAllocationInfoCollection.isInstantiatedDirectly(clazz)); }
diff --git a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java index b7b2513..a578485 100644 --- a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
@@ -62,7 +62,8 @@ SingleResolutionResult resolution = appView .appInfo() - .resolveMethodOn(clazz, appView.dexItemFactory().objectMembers.finalize) + .resolveMethodOnLegacy( + clazz, appView.dexItemFactory().objectMembers.finalize) .asSingleResolution(); if (resolution != null && resolution.getResolvedHolder().isProgramClass()) { return TraversalContinuation.doBreak();
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java index 3b96973..471f865 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -91,6 +91,7 @@ import java.util.Set; import java.util.Stack; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -129,6 +130,7 @@ private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>(); private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>(); private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet(); + private final Map<DexMethod, ProgramMethod> keptMethodBridges = new ConcurrentHashMap<>(); private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems = new ConcurrentLinkedQueue<>(); private final InternalOptions options; @@ -420,7 +422,7 @@ DexEncodedMethod target = appView .appInfo() - .unsafeResolveMethodDueToDexFormat(referenceInSubType) + .unsafeResolveMethodDueToDexFormatLegacy(referenceInSubType) .getSingleTarget(); // But, the resolution should not be landed on the current type we are visiting. if (target == null || target.getHolderType() == type) { @@ -574,7 +576,9 @@ visitAllSuperInterfaces(iface); } if (!clazz.isInterface()) { - visitAllSuperInterfaces(clazz.superType); + if (clazz.superType != null) { + visitAllSuperInterfaces(clazz.superType); + } return; } if (originalClazz == clazz) { @@ -601,7 +605,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOn(originalClazz, method.getReference()) + .resolveMethodOnLegacy(originalClazz, method.getReference()) .asSingleResolution(); if (resolutionResult == null || !resolutionResult.isVirtualTarget()) { return; @@ -614,16 +618,24 @@ // TODO(b/143643942): For fullmode, this check should probably be removed. return; } - ProgramMethod resolutionMethod = - new ProgramMethod( - resolutionResult.getResolvedHolder().asProgramClass(), - resolutionResult.getResolvedMethod()); - ProgramMethod methodToKeep = - canInsertForwardingMethod(originalClazz, resolutionMethod.getDefinition()) - ? new ProgramMethod( - originalClazz, - resolutionMethod.getDefinition().toForwardingMethod(originalClazz, appView)) - : resolutionMethod; + ProgramMethod resolutionMethod = resolutionResult.getResolvedProgramMethod(); + ProgramMethod methodToKeep; + if (canInsertForwardingMethod(originalClazz, resolutionMethod.getDefinition())) { + DexMethod methodToKeepReference = + resolutionMethod.getReference().withHolder(originalClazz, appView.dexItemFactory()); + methodToKeep = + keptMethodBridges.computeIfAbsent( + methodToKeepReference, + k -> + new ProgramMethod( + originalClazz, + resolutionMethod + .getDefinition() + .toForwardingMethod(originalClazz, appView))); + assert methodToKeepReference.equals(methodToKeep.getReference()); + } else { + methodToKeep = resolutionMethod; + } delayedRootSetActionItems.add( new InterfaceMethodSyntheticBridgeAction(
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index 9797f72..5aa62c3 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -853,7 +853,7 @@ // Conservatively find all possible targets for this method. LookupResultSuccess lookupResult = appInfo - .resolveMethodOnInterface(method.getHolderType(), method.getReference()) + .resolveMethodOnInterfaceLegacy(method.getHolderType(), method.getReference()) .lookupVirtualDispatchTargets(target, appInfo) .asLookupResultSuccess(); assert lookupResult != null; @@ -1568,7 +1568,7 @@ // Returns the method that shadows the given method, or null if method is not shadowed. private DexEncodedMethod findMethodInTarget(DexEncodedMethod method) { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(target, method.getReference()); + appInfo.resolveMethodOnLegacy(target, method.getReference()); if (!resolutionResult.isSingleResolution()) { // May happen in case of missing classes, or if multiple implementations were found. abortMerge = true; @@ -2133,8 +2133,8 @@ MethodResolutionResult resolutionResult = isInterface.isUnknown() - ? appView.appInfo().unsafeResolveMethodDueToDexFormat(method) - : appView.appInfo().resolveMethod(method, isInterface.isTrue()); + ? appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(method) + : appView.appInfo().resolveMethodLegacy(method, isInterface.isTrue()); if (!resolutionResult.isSingleResolution() || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) { setResult(true);
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 cee58c4..f6d8354 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -569,7 +569,7 @@ SynthesizingContext outerContext = internalGetOuterContext(context, appView); DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); DexClass clazz = appView.definitionFor(type); - assert clazz != null; + assert clazz != null : "Missing existing fixed class " + type; assert isSyntheticClass(type); assert clazz.isProgramClass(); return clazz.asProgramClass();
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java index c5ffc7a..1d74048 100644 --- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java +++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -246,7 +246,7 @@ DexClassAndMethod superTarget = appInfo() - .resolveMethodOn(method.getHolder(), method.getReference()) + .resolveMethodOnLegacy(method.getHolder(), method.getReference()) .lookupInvokeSpecialTarget(method.getHolder(), appInfo()); if (superTarget != null && !superTarget.isProgramMethod() @@ -267,7 +267,8 @@ method -> { DexClassAndMethod resolvedMethod = appInfo() - .resolveMethodOn(superType, method.getReference(), superType != clazz.superType) + .resolveMethodOnLegacy( + superType, method.getReference(), superType != clazz.superType) .getResolutionPair(); if (resolvedMethod != null && !resolvedMethod.isProgramMethod() @@ -311,7 +312,7 @@ assert lookupResult.getType().isStatic(); DexMethod rewrittenMethod = lookupResult.getReference(); handleRewrittenMethodResolution( - rewrittenMethod, appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod)); + rewrittenMethod, appInfo().unsafeResolveMethodDueToDexFormatLegacy(rewrittenMethod)); } @Override @@ -320,7 +321,7 @@ assert lookupResult.getType().isSuper(); DexMethod rewrittenMethod = lookupResult.getReference(); MethodResolutionResult resolutionResult = - appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod); + appInfo().unsafeResolveMethodDueToDexFormatLegacy(rewrittenMethod); if (resolutionResult.isFailedResolution() && resolutionResult.asFailedResolution().hasMethodsCausingError()) { handleRewrittenMethodResolution(rewrittenMethod, resolutionResult); @@ -349,8 +350,8 @@ handleRewrittenMethodResolution( method, lookupResult.getType().isInterface() - ? appInfo().resolveMethodOnInterfaceHolder(method) - : appInfo().resolveMethodOnClassHolder(method)); + ? appInfo().resolveMethodOnInterfaceHolderLegacy(method) + : appInfo().resolveMethodOnClassHolderLegacy(method)); } private void handleRewrittenMethodResolution(
diff --git a/src/main/java/com/android/tools/r8/utils/Box.java b/src/main/java/com/android/tools/r8/utils/Box.java index 2e6f17c..1134718 100644 --- a/src/main/java/com/android/tools/r8/utils/Box.java +++ b/src/main/java/com/android/tools/r8/utils/Box.java
@@ -5,65 +5,43 @@ package com.android.tools.r8.utils; import java.util.Comparator; -import java.util.Objects; import java.util.function.Supplier; -public class Box<T> { - - private T value; +public class Box<T> extends BoxBase<T> { public Box() {} public Box(T initialValue) { - set(initialValue); + super(initialValue); } + @Override + public void clear() { + super.clear(); + } + + @Override public T computeIfAbsent(Supplier<T> supplier) { - if (value == null) { - value = supplier.get(); - } - return value; + return super.computeIfAbsent(supplier); } + @Override public T get() { - return value; + return super.get(); } - public void set(T value) { - this.value = value; - } - - public void setMin(T element, Comparator<T> comparator) { - if (!isSet() || comparator.compare(element, get()) < 0) { - set(element); - } - } - - public boolean isSet() { - return value != null; - } - + @Override public T getAndSet(T newValue) { - T oldValue = value; - value = newValue; - return oldValue; + return super.getAndSet(newValue); } @Override - public boolean equals(Object object) { - if (object == null || getClass() != object.getClass()) { - return false; - } - Box<?> box = (Box<?>) object; - return Objects.equals(value, box.value); + public void set(T value) { + super.set(value); } @Override - public int hashCode() { - return Objects.hashCode(value); - } - - public void empty() { - value = null; + public void setMin(T value, Comparator<T> comparator) { + super.setMin(value, comparator); } }
diff --git a/src/main/java/com/android/tools/r8/utils/BoxBase.java b/src/main/java/com/android/tools/r8/utils/BoxBase.java new file mode 100644 index 0000000..d38d92b --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/BoxBase.java
@@ -0,0 +1,69 @@ +// 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.utils; + +import java.util.Comparator; +import java.util.Objects; +import java.util.function.Supplier; + +public abstract class BoxBase<T> { + + private T value; + + public BoxBase() {} + + public BoxBase(T initialValue) { + set(initialValue); + } + + void clear() { + set(null); + } + + T computeIfAbsent(Supplier<T> supplier) { + if (!isSet()) { + set(supplier.get()); + } + return value; + } + + T get() { + return value; + } + + T getAndSet(T newValue) { + T oldValue = value; + value = newValue; + return oldValue; + } + + void set(T value) { + this.value = value; + } + + void setMin(T value, Comparator<T> comparator) { + if (!isSet() || comparator.compare(value, get()) < 0) { + set(value); + } + } + + public boolean isSet() { + return value != null; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) { + return false; + } + BoxBase<?> box = (BoxBase<?>) object; + return Objects.equals(value, box.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java b/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java index c9a56da..c16b39f 100644 --- a/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java +++ b/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
@@ -13,12 +13,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.function.Function; -public abstract class DepthFirstSearchWorkListBase<N, T extends DFSNodeImpl<N>> { +public abstract class DepthFirstSearchWorkListBase<N, T extends DFSNodeImpl<N>, TB, TC> { public interface DFSNode<N> { N getNode(); @@ -103,36 +104,48 @@ } private final ArrayDeque<T> workList = new ArrayDeque<>(); + // This map is necessary ensure termination since we embed nodes into nodes with state. + private final Map<N, T> nodeToNodeWithStateMap = new IdentityHashMap<>(); abstract T createDfsNode(N node); /** The initial processing of a node during forward search */ - abstract TraversalContinuation<?, ?> internalOnVisit(T node); + abstract TraversalContinuation<TB, TC> internalOnVisit(T node); /** The joining of state during backtracking of the algorithm. */ - abstract TraversalContinuation<?, ?> internalOnJoin(T node); + abstract TraversalContinuation<TB, TC> internalOnJoin(T node); + + abstract List<TC> getFinalStateForRoots(Collection<N> roots); final T internalEnqueueNode(N value) { - T dfsNode = createDfsNode(value); + T dfsNode = nodeToNodeWithStateMap.computeIfAbsent(value, this::createDfsNode); if (dfsNode.isNotProcessed()) { workList.addLast(dfsNode); } return dfsNode; } + protected T getNodeStateForNode(N value) { + return nodeToNodeWithStateMap.get(value); + } + + public final TraversalContinuation<TB, TC> run(N root) { + return run(Collections.singletonList(root)).map(Function.identity(), results -> results.get(0)); + } + @SafeVarargs - public final TraversalContinuation<?, ?> run(N... roots) { + public final TraversalContinuation<TB, List<TC>> run(N... roots) { return run(Arrays.asList(roots)); } - public final TraversalContinuation<?, ?> run(Collection<N> roots) { + public final TraversalContinuation<TB, List<TC>> run(Collection<N> roots) { roots.forEach(this::internalEnqueueNode); - TraversalContinuation<?, ?> continuation = TraversalContinuation.doContinue(); while (!workList.isEmpty()) { T node = workList.removeLast(); if (node.isFinished()) { continue; } + TraversalContinuation<TB, TC> continuation; if (node.isNotProcessed()) { workList.addLast(node); node.setWaiting(); @@ -143,14 +156,15 @@ node.setFinished(); } if (continuation.shouldBreak()) { - return continuation; + return TraversalContinuation.doBreak(continuation.asBreak().getValue()); } } - return continuation; + + return TraversalContinuation.doContinue(getFinalStateForRoots(roots)); } - public abstract static class DepthFirstSearchWorkList<N> - extends DepthFirstSearchWorkListBase<N, DFSNodeImpl<N>> { + public abstract static class DepthFirstSearchWorkList<N, TB, TC> + extends DepthFirstSearchWorkListBase<N, DFSNodeImpl<N>, TB, TC> { /** * The initial processing of the node when visiting the first time during the depth first @@ -161,7 +175,7 @@ * before but not finished there is a cycle. * @return A value describing if the DFS algorithm should continue to run. */ - protected abstract TraversalContinuation<?, ?> process( + protected abstract TraversalContinuation<TB, TC> process( DFSNode<N> node, Function<N, DFSNode<N>> childNodeConsumer); @Override @@ -170,18 +184,18 @@ } @Override - TraversalContinuation<?, ?> internalOnVisit(DFSNodeImpl<N> node) { + TraversalContinuation<TB, TC> internalOnVisit(DFSNodeImpl<N> node) { return process(node, this::internalEnqueueNode); } @Override - protected TraversalContinuation<?, ?> internalOnJoin(DFSNodeImpl<N> node) { + protected TraversalContinuation<TB, TC> internalOnJoin(DFSNodeImpl<N> node) { return TraversalContinuation.doContinue(); } } - public abstract static class StatefulDepthFirstSearchWorkList<N, S> - extends DepthFirstSearchWorkListBase<N, DFSNodeWithStateImpl<N, S>> { + public abstract static class StatefulDepthFirstSearchWorkList<N, S, TB> + extends DepthFirstSearchWorkListBase<N, DFSNodeWithStateImpl<N, S>, TB, S> { private final Map<DFSNodeWithStateImpl<N, S>, List<DFSNodeWithState<N, S>>> childStateMap = new IdentityHashMap<>(); @@ -195,7 +209,7 @@ * before but not finished there is a cycle. * @return A value describing if the DFS algorithm should continue to run. */ - protected abstract TraversalContinuation<?, ?> process( + protected abstract TraversalContinuation<TB, S> process( DFSNodeWithState<N, S> node, Function<N, DFSNodeWithState<N, S>> childNodeConsumer); /** @@ -205,7 +219,7 @@ * @param childStates The already computed child states. * @return A value describing if the DFS algorithm should continue to run. */ - protected abstract TraversalContinuation<?, ?> joiner( + protected abstract TraversalContinuation<TB, S> joiner( DFSNodeWithState<N, S> node, List<DFSNodeWithState<N, S>> childStates); @Override @@ -214,7 +228,7 @@ } @Override - TraversalContinuation<?, ?> internalOnVisit(DFSNodeWithStateImpl<N, S> node) { + TraversalContinuation<TB, S> internalOnVisit(DFSNodeWithStateImpl<N, S> node) { List<DFSNodeWithState<N, S>> childStates = new ArrayList<>(); List<DFSNodeWithState<N, S>> removedChildStates = childStateMap.put(node, childStates); assert removedChildStates == null; @@ -228,7 +242,7 @@ } @Override - protected TraversalContinuation<?, ?> internalOnJoin(DFSNodeWithStateImpl<N, S> node) { + protected TraversalContinuation<TB, S> internalOnJoin(DFSNodeWithStateImpl<N, S> node) { return joiner( node, childStateMap.computeIfAbsent( @@ -238,5 +252,10 @@ return new ArrayList<>(); })); } + + @Override + List<S> getFinalStateForRoots(Collection<N> roots) { + return ListUtils.map(roots, root -> getNodeStateForNode(root).state); + } } }
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java deleted file mode 100644 index 00412d9..0000000 --- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java +++ /dev/null
@@ -1,313 +0,0 @@ -// Copyright (c) 2018, 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.utils; - -import com.android.tools.r8.ArchiveClassFileProvider; -import com.android.tools.r8.DiagnosticsHandler; -import com.android.tools.r8.Keep; -import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar; -import com.android.tools.r8.origin.PathOrigin; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Provides a mappings of classes to modules. The structure of the input file is as follows: - * packageOrClass:module - * - * <p>Lines with a # prefix are ignored. - * - * <p>We will do most specific matching, i.e., - * <pre> - * com.google.foobar.*:feature2 - * com.google.*:base - * </pre> - * will put everything in the com.google namespace into base, except classes in com.google.foobar - * that will go to feature2. Class based mappings takes precedence over packages (since they are - * more specific): - * <pre> - * com.google.A:feature2 - * com.google.*:base - * </pre> - * Puts A into feature2, and all other classes from com.google into base. - * - * <p>Note that this format does not allow specifying inter-module dependencies, this is simply a - * placement tool. - */ -@Keep -public final class FeatureClassMapping { - - Map<String, String> parsedRules = new HashMap<>(); // Already parsed rules. - Map<String, String> parseNonClassRules = new HashMap<>(); - boolean usesOnlyExactMappings = true; - - Set<FeaturePredicate> mappings = new HashSet<>(); - - Path mappingFile; - String baseName = DEFAULT_BASE_NAME; - - static final String DEFAULT_BASE_NAME = "base"; - - static final String COMMENT = "#"; - static final String SEPARATOR = ":"; - - public String getBaseName() { - return baseName; - } - - private static class SpecificationOrigin extends PathOrigin { - - public SpecificationOrigin(Path path) { - super(path); - } - - @Override - public String part() { - return "specification file '" + super.part() + "'"; - } - } - - private static class JarFileOrigin extends PathOrigin { - - public JarFileOrigin(Path path) { - super(path); - } - - @Override - public String part() { - return "jar file '" + super.part() + "'"; - } - } - - public static FeatureClassMapping fromSpecification(Path file) throws FeatureMappingException { - return fromSpecification(file, new DiagnosticsHandler() {}); - } - - public static FeatureClassMapping fromSpecification(Path file, DiagnosticsHandler reporter) - throws FeatureMappingException { - FeatureClassMapping mapping = new FeatureClassMapping(); - List<String> lines = null; - try { - lines = FileUtils.readAllLines(file); - } catch (IOException e) { - ExceptionDiagnostic error = new ExceptionDiagnostic(e, new SpecificationOrigin(file)); - reporter.error(error); - throw new AbortException(error); - } - for (int i = 0; i < lines.size(); i++) { - String line = lines.get(i); - mapping.parseAndAdd(line, i); - } - return mapping; - } - - public static class Internal { - private static List<String> getClassFileDescriptors(String jar, DiagnosticsHandler reporter) { - Path jarPath = Paths.get(jar); - try { - return new ArchiveClassFileProvider(jarPath).getClassDescriptors() - .stream() - .map(DescriptorUtils::descriptorToJavaType) - .collect(Collectors.toList()); - } catch (IOException e) { - ExceptionDiagnostic error = new ExceptionDiagnostic(e, new JarFileOrigin(jarPath)); - reporter.error(error); - throw new AbortException(error); - } - } - - private static List<String> getNonClassFiles(String jar, DiagnosticsHandler reporter) { - try (ZipFile zipfile = new ZipFile(jar, StandardCharsets.UTF_8)) { - return zipfile.stream() - .filter(entry -> !ZipUtils.isClassFile(entry.getName())) - .map(ZipEntry::getName) - .collect(Collectors.toList()); - } catch (IOException e) { - ExceptionDiagnostic error = new ExceptionDiagnostic(e, new JarFileOrigin(Paths.get(jar))); - reporter.error(error); - throw new AbortException(error); - } - } - - public static FeatureClassMapping fromJarFiles( - List<FeatureJar> featureJars, List<String> baseJars, String baseName, - DiagnosticsHandler reporter) - throws FeatureMappingException { - FeatureClassMapping mapping = new FeatureClassMapping(); - if (baseName != null) { - mapping.baseName = baseName; - } - for (FeatureJar featureJar : featureJars) { - for (String javaType : getClassFileDescriptors(featureJar.getJar(), reporter)) { - mapping.addMapping(javaType, featureJar.getOutputName()); - } - for (String nonClass : getNonClassFiles(featureJar.getJar(), reporter)) { - mapping.addNonClassMapping(nonClass, featureJar.getOutputName()); - } - } - for (String baseJar : baseJars) { - for (String javaType : getClassFileDescriptors(baseJar, reporter)) { - mapping.addBaseMapping(javaType); - } - for (String nonClass : getNonClassFiles(baseJar, reporter)) { - mapping.addBaseNonClassMapping(nonClass); - } - } - assert mapping.usesOnlyExactMappings; - return mapping; - } - - } - - private FeatureClassMapping() {} - - public void addBaseMapping(String clazz) throws FeatureMappingException { - addMapping(clazz, baseName); - } - - public void addBaseNonClassMapping(String name) { - addNonClassMapping(name, baseName); - } - - public void addMapping(String clazz, String feature) throws FeatureMappingException { - addRule(clazz, feature, 0); - } - - public void addNonClassMapping(String name, String feature) { - // If a non-class file is present in multiple features put the resource in the base. - parseNonClassRules.put(name, parseNonClassRules.containsKey(name) ? baseName : feature); - } - - FeatureClassMapping(List<String> lines) throws FeatureMappingException { - for (int i = 0; i < lines.size(); i++) { - String line = lines.get(i); - parseAndAdd(line, i); - } - } - - public String featureForClass(String clazz) { - if (usesOnlyExactMappings) { - return parsedRules.getOrDefault(clazz, baseName); - } else { - FeaturePredicate bestMatch = null; - for (FeaturePredicate mapping : mappings) { - if (mapping.match(clazz)) { - if (bestMatch == null || bestMatch.predicate.length() < mapping.predicate.length()) { - bestMatch = mapping; - } - } - } - if (bestMatch == null) { - return baseName; - } - return bestMatch.feature; - } - } - - public String featureForNonClass(String nonClass) { - return parseNonClassRules.getOrDefault(nonClass, baseName); - } - - private void parseAndAdd(String line, int lineNumber) throws FeatureMappingException { - if (line.startsWith(COMMENT)) { - return; // Ignore comments - } - if (line.isEmpty()) { - return; // Ignore blank lines - } - - if (!line.contains(SEPARATOR)) { - error("Mapping lines must contain a " + SEPARATOR, lineNumber); - } - String[] values = line.split(SEPARATOR); - if (values.length != 2) { - error("Mapping lines can only contain one " + SEPARATOR, lineNumber); - } - - String predicate = values[0]; - String feature = values[1]; - addRule(predicate, feature, lineNumber); - } - - private void addRule(String predicate, String feature, int lineNumber) - throws FeatureMappingException { - if (parsedRules.containsKey(predicate)) { - if (!parsedRules.get(predicate).equals(feature)) { - error("Redefinition of predicate " + predicate + "not allowed", lineNumber); - } - return; // Already have this rule. - } - parsedRules.put(predicate, feature); - FeaturePredicate featurePredicate = new FeaturePredicate(predicate, feature); - mappings.add(featurePredicate); - usesOnlyExactMappings &= featurePredicate.isExactmapping(); - } - - private void error(String error, int line) throws FeatureMappingException { - throw new FeatureMappingException( - "Invalid mappings specification: " + error + "\n in file " + mappingFile + ":" + line); - } - - @Keep - public static class FeatureMappingException extends Exception { - FeatureMappingException(String message) { - super(message); - } - } - - /** A feature predicate can either be a wildcard or class predicate. */ - private static class FeaturePredicate { - private static Pattern identifier = Pattern.compile("[A-Za-z_\\-][A-Za-z0-9_$\\-]*"); - final String predicate; - final String feature; - final boolean isCatchAll; - // False implies class predicate. - final boolean isWildcard; - - FeaturePredicate(String predicate, String feature) throws FeatureMappingException { - isWildcard = predicate.endsWith(".*"); - isCatchAll = predicate.equals("*"); - if (isCatchAll) { - this.predicate = ""; - } else if (isWildcard) { - String packageName = predicate.substring(0, predicate.length() - 2); - if (!DescriptorUtils.isValidJavaType(packageName)) { - throw new FeatureMappingException(packageName + " is not a valid identifier"); - } - // Prefix of a fully-qualified class name, including a terminating dot. - this.predicate = predicate.substring(0, predicate.length() - 1); - } else { - if (!DescriptorUtils.isValidJavaType(predicate)) { - throw new FeatureMappingException(predicate + " is not a valid identifier"); - } - this.predicate = predicate; - } - this.feature = feature; - } - - boolean match(String className) { - if (isCatchAll) { - return true; - } else if (isWildcard) { - return className.startsWith(predicate); - } else { - return className.equals(predicate); - } - } - - boolean isExactmapping() { - return !isWildcard && !isCatchAll; - } - } -}
diff --git a/src/main/java/com/android/tools/r8/utils/IntBox.java b/src/main/java/com/android/tools/r8/utils/IntBox.java index 9a31ebd..79684cd 100644 --- a/src/main/java/com/android/tools/r8/utils/IntBox.java +++ b/src/main/java/com/android/tools/r8/utils/IntBox.java
@@ -53,6 +53,15 @@ value += i; } + public int incrementAndGet() { + return incrementAndGet(1); + } + + public int incrementAndGet(int i) { + increment(i); + return get(); + } + public void set(int value) { this.value = value; }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 8942e41..a2c6aae 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -360,6 +360,7 @@ public boolean enableRedundantFieldLoadElimination = true; // TODO(b/138917494): Disable until we have numbers on potential performance penalties. public boolean enableRedundantConstNumberOptimization = false; + public boolean enableLoopUnrolling = true; public String synthesizedClassPrefix = ""; @@ -1671,6 +1672,10 @@ public void disableApiCallerIdentification() { enableApiCallerIdentification = false; } + + public void disableSubbingOfClasses() { + enableStubbingOfClasses = false; + } } public static class ProtoShrinkingOptions { @@ -1815,7 +1820,6 @@ public boolean enableEnumUnboxingDebugLogs = false; public boolean forceRedundantConstNumberRemoval = false; public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false; - public boolean enableExperimentalLoopUnrolling = false; public boolean invertConditionals = false; public boolean placeExceptionalBlocksLast = false; public boolean dontCreateMarkerInD8 = false;
diff --git a/src/main/java/com/android/tools/r8/utils/LazyBox.java b/src/main/java/com/android/tools/r8/utils/LazyBox.java index 38d1f2a..0a25b99 100644 --- a/src/main/java/com/android/tools/r8/utils/LazyBox.java +++ b/src/main/java/com/android/tools/r8/utils/LazyBox.java
@@ -6,7 +6,7 @@ import java.util.function.Supplier; -public class LazyBox<T> extends Box<T> { +public class LazyBox<T> extends BoxBase<T> { private final Supplier<T> supplier;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java index 78f612a..f277168 100644 --- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java +++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -72,7 +72,6 @@ import com.android.tools.r8.retrace.internal.RetraceUtils; import com.android.tools.r8.shaking.KeepInfoCollection; import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; -import com.google.common.base.Suppliers; import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -87,7 +86,6 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.function.Function; -import java.util.function.Supplier; public class LineNumberOptimizer { @@ -502,8 +500,8 @@ // Create a supplier which creates a new, cached ClassNaming.Builder on-demand. DexType originalType = appView.graphLens().getOriginalType(clazz.type); DexString renamedDescriptor = namingLens.lookupDescriptor(clazz.getType()); - Supplier<ClassNaming.Builder> onDemandClassNamingBuilder = - Suppliers.memoize( + LazyBox<ClassNaming.Builder> onDemandClassNamingBuilder = + new LazyBox<>( () -> classNameMapperBuilder.classNamingBuilder( DescriptorUtils.descriptorToJavaType(renamedDescriptor.toString()), @@ -516,14 +514,14 @@ String sourceFile = originalSourceFile.toString(); if (!RetraceUtils.hasPredictableSourceFileName(clazz.toSourceString(), sourceFile)) { onDemandClassNamingBuilder - .get() + .computeIfAbsent() .addMappingInformation(FileNameInformation.build(sourceFile), Unreachable::raise); } } if (isSyntheticClass) { onDemandClassNamingBuilder - .get() + .computeIfAbsent() .addMappingInformation( CompilerSynthesizedMappingInformation.builder().build(), Unreachable::raise); } @@ -621,13 +619,13 @@ } MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName); - onDemandClassNamingBuilder.get().addMemberEntry(memberNaming); + onDemandClassNamingBuilder.computeIfAbsent().addMemberEntry(memberNaming); // Add simple "a() -> b" mapping if we won't have any other with concrete line numbers if (mappedPositions.isEmpty()) { MappedRange range = onDemandClassNamingBuilder - .get() + .computeIfAbsent() .addMappedRange(null, originalSignature, null, obfuscatedName); methodMappingInfo.forEach( info -> range.addMappingInformation(info, Unreachable::raise)); @@ -702,7 +700,7 @@ obfuscatedRange = new Range(firstPosition.obfuscatedLine, lastPosition.obfuscatedLine); } - ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.get(); + ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.computeIfAbsent(); MappedRange lastMappedRange = getMappedRangesForPosition( appView.options().dexItemFactory(), @@ -895,11 +893,11 @@ private static void addClassToClassNaming( DexType originalType, DexString renamedClassName, - Supplier<Builder> onDemandClassNamingBuilder) { + LazyBox<Builder> onDemandClassNamingBuilder) { // We do know we need to create a ClassNaming.Builder if the class itself had been renamed. if (originalType.descriptor != renamedClassName) { // Not using return value, it's registered in classNameMapperBuilder - onDemandClassNamingBuilder.get(); + onDemandClassNamingBuilder.computeIfAbsent(); } } @@ -908,7 +906,7 @@ NamingLens namingLens, DexProgramClass clazz, DexType originalType, - Supplier<Builder> onDemandClassNamingBuilder) { + LazyBox<Builder> onDemandClassNamingBuilder) { clazz.forEachField( dexEncodedField -> { DexField dexField = dexEncodedField.getReference(); @@ -918,7 +916,7 @@ FieldSignature originalSignature = FieldSignature.fromDexField(originalField, originalField.holder != originalType); MemberNaming memberNaming = new MemberNaming(originalSignature, renamedName.toString()); - onDemandClassNamingBuilder.get().addMemberEntry(memberNaming); + onDemandClassNamingBuilder.computeIfAbsent().addMemberEntry(memberNaming); } }); }
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java index 19f89d8..c33d020 100644 --- a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java +++ b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.utils; import com.android.tools.r8.errors.Unreachable; +import java.util.function.Function; /** Two value continuation value to indicate the continuation of a loop/traversal. */ /* This class is used for building up api class member traversals. */ @@ -25,14 +26,29 @@ return null; } + public <TBx, TCx> TraversalContinuation<TBx, TCx> map( + Function<TB, TBx> mapBreak, Function<TC, TCx> mapContinue) { + if (isBreak()) { + return new Break<>(mapBreak.apply(asBreak().getValue())); + } else { + assert isContinue(); + return new Continue<>(mapContinue.apply(asContinue().getValue())); + } + } + public static class Continue<TB, TC> extends TraversalContinuation<TB, TC> { - private static final TraversalContinuation<?, ?> CONTINUE_NO_VALUE = + private static final TraversalContinuation.Continue<?, ?> CONTINUE_NO_VALUE = new Continue<Object, Object>(null) { @Override public Object getValue() { return new Unreachable( "Invalid attempt at getting a value from a no-value continue state."); } + + @Override + public Object getValueOrDefault(Object defaultValue) { + return defaultValue; + } }; private final TC value; @@ -45,6 +61,10 @@ return value; } + public TC getValueOrDefault(TC defaultValue) { + return value; + } + @Override public boolean isContinue() { return true; @@ -57,13 +77,18 @@ } public static class Break<TB, TC> extends TraversalContinuation<TB, TC> { - private static final TraversalContinuation<?, ?> BREAK_NO_VALUE = + private static final TraversalContinuation.Break<?, ?> BREAK_NO_VALUE = new Break<Object, Object>(null) { @Override public Object getValue() { return new Unreachable( "Invalid attempt at getting a value from a no-value break state."); } + + @Override + public Object getValueOrDefault(Object defaultValue) { + return defaultValue; + } }; private final TB value; @@ -76,6 +101,10 @@ return value; } + public TB getValueOrDefault(TB defaultValue) { + return value; + } + @Override public boolean isBreak() { return true; @@ -87,29 +116,34 @@ } } - public static TraversalContinuation<?, ?> breakIf(boolean condition) { + public static <TB, TC> TraversalContinuation<TB, TC> breakIf(boolean condition) { return continueIf(!condition); } - public static TraversalContinuation<?, ?> continueIf(boolean condition) { + public static <TB, TC> TraversalContinuation<TB, TC> continueIf(boolean condition) { return condition ? doContinue() : doBreak(); } - @SuppressWarnings("unchecked") - public static <TB, TC> TraversalContinuation<TB, TC> doContinue() { - return (TraversalContinuation<TB, TC>) Continue.CONTINUE_NO_VALUE; + public TraversalContinuation<TB, TC> ifContinueThen( + Function<TraversalContinuation.Continue<TB, TC>, TraversalContinuation<TB, TC>> fn) { + return isContinue() ? fn.apply(asContinue()) : this; } - public static <TB, TC> TraversalContinuation<TB, TC> doContinue(TC value) { + @SuppressWarnings("unchecked") + public static <TB, TC> TraversalContinuation.Continue<TB, TC> doContinue() { + return (TraversalContinuation.Continue<TB, TC>) Continue.CONTINUE_NO_VALUE; + } + + public static <TB, TC> TraversalContinuation.Continue<TB, TC> doContinue(TC value) { return new Continue<>(value); } @SuppressWarnings("unchecked") - public static <TB, TC> TraversalContinuation<TB, TC> doBreak() { - return (TraversalContinuation<TB, TC>) Break.BREAK_NO_VALUE; + public static <TB, TC> TraversalContinuation.Break<TB, TC> doBreak() { + return (TraversalContinuation.Break<TB, TC>) Break.BREAK_NO_VALUE; } - public static <TB, TC> TraversalContinuation<TB, TC> doBreak(TB value) { + public static <TB, TC> TraversalContinuation.Break<TB, TC> doBreak(TB value) { return new Break<>(value); }
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalUtils.java b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java new file mode 100644 index 0000000..25e1be0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
@@ -0,0 +1,57 @@ +// 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.utils; + +import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument; + +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +public class TraversalUtils { + + public static <BT, CT> BT getFirst( + Function<Function<BT, TraversalContinuation<BT, CT>>, TraversalContinuation<BT, CT>> + traversal) { + return traversal.apply(TraversalContinuation::doBreak).asBreak().getValue(); + } + + public static <BT, CT> boolean isSingleton( + Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal) { + return isSizeExactly(traversal, 1); + } + + public static <BT, CT> boolean isSizeExactly( + Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal, int value) { + IntBox counter = new IntBox(); + traversal.accept( + ignoreArgument(() -> TraversalContinuation.breakIf(counter.incrementAndGet() > value))); + return counter.get() == value; + } + + public static <BT, CT> boolean isSizeGreaterThan( + Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal, int value) { + IntBox counter = new IntBox(); + traversal.accept( + ignoreArgument(() -> TraversalContinuation.breakIf(counter.incrementAndGet() > value))); + return counter.get() > value; + } + + public static <S, BT, CT> TraversalContinuation<BT, CT> traverseIterable( + Iterable<S> iterable, + BiFunction<? super S, ? super CT, TraversalContinuation<BT, CT>> fn, + CT initialValue) { + TraversalContinuation<BT, CT> traversalContinuation = + TraversalContinuation.doContinue(initialValue); + for (S element : iterable) { + traversalContinuation = + fn.apply(element, traversalContinuation.asContinue().getValueOrDefault(null)); + if (traversalContinuation.isBreak()) { + break; + } + } + return traversalContinuation; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java index 7dc7b3e..32ab490 100644 --- a/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java +++ b/src/main/java/com/android/tools/r8/utils/classhierarchy/MethodOverridesCollector.java
@@ -111,7 +111,7 @@ SingleResolutionResult<?> resolutionResult = appView .appInfo() - .resolveMethodOnClass(implementer, interfaceMethod) + .resolveMethodOnClassLegacy(implementer, interfaceMethod) .asSingleResolution(); if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) { continue;
diff --git a/src/main/keep.txt b/src/main/keep.txt index e376924..deb8e6d 100644 --- a/src/main/keep.txt +++ b/src/main/keep.txt
@@ -12,7 +12,6 @@ -keep public class com.android.tools.r8.D8 { public static void main(java.lang.String[]); } -keep public class com.android.tools.r8.R8 { public static void main(java.lang.String[]); } -keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); } --keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); } -keep public class com.android.tools.r8.Version { public static final java.lang.String LABEL; } -keep public class com.android.tools.r8.Version { public static java.lang.String getVersionString(); }
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java index 318b3c2..8ba7823 100644 --- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java +++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -111,7 +111,7 @@ testBuilder .run(parameters.getRuntime(), "MySubscriber") .applyIf( - parameters.asDexRuntime().getVersion().isOlderThan(DexVm.Version.V13_MASTER), + parameters.asDexRuntime().getVersion().isOlderThan(DexVm.Version.V13_0_0), b -> b.assertFailureWithErrorThatMatches( anyOf(
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java index cf27d33..1ec4d18 100644 --- a/src/test/java/com/android/tools/r8/D8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -123,13 +123,10 @@ return self(); } - // TODO(b/183125319): Make this the default as part of API support in D8. public D8TestBuilder internalEnableMappingOutput() { assert proguardMapOutputBuilder == null; proguardMapOutputBuilder = new StringBuilder(); - // TODO(b/183125319): Use the API once supported in D8. - addOptionsModification( - o -> o.proguardMapConsumer = (s, h) -> proguardMapOutputBuilder.append(s)); + getBuilder().setProguardMapConsumer((s, h) -> proguardMapOutputBuilder.append(s)); return self(); } }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java index c860721..10c5358 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -102,7 +102,7 @@ DexVm.Version.V9_0_0, DexVm.Version.V10_0_0, DexVm.Version.V12_0_0, - DexVm.Version.V13_MASTER); + DexVm.Version.V13_0_0); private static final String JUNIT_TEST_RUNNER = "org.junit.runner.JUnitCore"; private static final String JUNIT_JAR = "third_party/junit/junit-4.13-beta-2.jar"; @@ -186,13 +186,13 @@ .put( "098-ddmc", TestCondition.match( - TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_MASTER))) + TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_0_0))) // TODO(b/197079442): Triage - fails with "java.lang.NoSuchMethodException: // org.apache.harmony.dalvik.ddmc.DdmVmInternal.enableRecentAllocations [boolean]" .put( "145-alloc-tracking-stress", TestCondition.match( - TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_MASTER))) + TestCondition.runtimes(DexVm.Version.V12_0_0, DexVm.Version.V13_0_0))) .build(); // Tests that are flaky with the Art version we currently use. @@ -499,7 +499,7 @@ ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder(); builder .put( - DexVm.Version.V13_MASTER, + DexVm.Version.V13_0_0, ImmutableList.of("454-get-vreg", "457-regs", "543-env-long-ref", "518-null-array-get")) .put( DexVm.Version.V12_0_0, @@ -848,7 +848,7 @@ DexVm.Version.V5_1_1, DexVm.Version.V6_0_1, DexVm.Version.V7_0_0, - DexVm.Version.V13_MASTER)), + DexVm.Version.V13_0_0)), TestCondition.match( TestCondition.compilers( CompilerUnderTest.R8,
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index 8478b17..a7759e3 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -90,7 +90,7 @@ // TODO(120402963) Triage. ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking")) .put( - Version.V13_MASTER, + Version.V13_0_0, // TODO(120402963) Triage. ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking")) .put(Version.DEFAULT, ImmutableList.of())
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java index 1d6cb9a..e1064b0 100644 --- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -15,7 +15,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -35,8 +34,79 @@ @Rule public ExpectedException thrown = ExpectedException.none(); + @Parameters(name = "{0}: {1}") + public static Collection<Object[]> data() { + return buildParameters( + getTestParameters().withDexRuntimes().withAllApiLevels().build(), tests.keySet()); + } + private static final String SMALI_DIR = ToolHelper.SMALI_BUILD_DIR; + private static Map<String, String> tests; + + static { + ImmutableMap.Builder<String, String> testsBuilder = ImmutableMap.builder(); + testsBuilder + .put( + "arithmetic", + StringUtils.lines( + "-1", "3", "2", "3", "3.0", "1", "0", "-131580", "-131580", "2", "4", "-2")) + .put( + "controlflow", + StringUtils.lines("2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2")) + .put("fibonacci", StringUtils.lines("55", "55", "55", "55")) + .put("fill-array-data", "[1, 2, 3][4, 5, 6]") + .put("filled-new-array", "[1, 2, 3][4, 5, 6][1, 2, 3, 4, 5, 6][6, 5, 4, 3, 2, 1]") + .put("packed-switch", "12345") + .put("sparse-switch", "12345") + .put("unreachable-code-1", "777") + .put( + "multiple-returns", + StringUtils.lines("TFtf", "1", "4611686018427387904", "true", "false")) + .put("try-catch", "") + .put("phi-removal-regression", StringUtils.lines("returnBoolean")) + .put( + "overlapping-long-registers", + StringUtils.lines("-9151314442816847872", "-9151314442816319488")) + .put( + "type-confusion-regression", + StringUtils.lines("java.lang.RuntimeException: Test.<init>()")) + .put( + "type-confusion-regression2", + StringUtils.lines("java.lang.NullPointerException: Attempt to read from null array")) + .put( + "type-confusion-regression3", + StringUtils.lines( + "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" + + " on a null object reference")) + .put("type-confusion-regression4", "") + .put( + "type-confusion-regression5", StringUtils.lines("java.lang.RuntimeException: getId()I")) + .put("chain-of-loops", StringUtils.lines("java.lang.RuntimeException: f(II)")) + .put("new-instance-and-init", StringUtils.lines("Test(0)", "Test(0)", "Test(0)")) + .put( + "bad-codegen", + StringUtils.lines( + "java.lang.NullPointerException: Attempt to read from field " + + "'Test Test.a' on a null object reference")) + .put( + "merge-blocks-regression", + StringUtils.lines( + "java.lang.NullPointerException: Attempt to invoke virtual" + + " method 'Test Test.bW_()' on a null object reference")) + .put("self-is-catch-block", StringUtils.lines("100", "-1")) + .put("infinite-loop", "") + .put( + "regression/33336471", + StringUtils.lines( + "START", "0", "2", "LOOP", "1", "2", "LOOP", "2", "2", "DONE", "START", "0", "2", + "LOOP", "1", "2", "LOOP", "2", "2", "DONE")) + .put("regression/33846227", "") + .put("illegal-invokes", StringUtils.lines("ICCE", "ICCE")) + .build(); + tests = testsBuilder.build(); + } + private static Map<String, Set<String>> missingClasses = ImmutableMap.of( "try-catch", ImmutableSet.of("test.X"), @@ -44,60 +114,63 @@ "bad-codegen", ImmutableSet.of("java.util.LTest")); // Tests where the original smali code fails on Art, but runs after R8 processing. - private static Map<DexVm.Version, List<String>> originalFailingOnArtVersions = ImmutableMap.of( - Version.V5_1_1, ImmutableList.of( - // Smali code contains an empty switch payload. - "sparse-switch", - "regression/33846227" - ), - Version.V4_4_4, ImmutableList.of( - // Smali code contains an empty switch payload. - "sparse-switch", - "regression/33846227" - ), - Version.V4_0_4, ImmutableList.of( - // Smali code contains an empty switch payload. - "sparse-switch", - "regression/33846227" - ) - ); + private static final Map<DexVm.Version, List<String>> originalFailingOnArtVersions = + ImmutableMap.of( + Version.V5_1_1, + ImmutableList.of( + // Smali code contains an empty switch payload. + "sparse-switch", "regression/33846227"), + Version.V4_4_4, + ImmutableList.of( + // Smali code contains an empty switch payload. + "sparse-switch", "regression/33846227"), + Version.V4_0_4, + ImmutableList.of( + // Smali code contains an empty switch payload. + "sparse-switch", "regression/33846227")); // Tests where the output has a different output than the original on certain VMs. - private static Map<DexVm.Version, Map<String, String>> customProcessedOutputExpectation = + private static final Map<DexVm.Version, Map<String, String>> customProcessedOutputExpectation = ImmutableMap.of( - Version.V4_4_4, ImmutableMap.of( - "bad-codegen", "java.lang.NullPointerException\n", - "type-confusion-regression2", "java.lang.NullPointerException\n", - "type-confusion-regression3", "java.lang.NullPointerException\n", - "merge-blocks-regression", "java.lang.NullPointerException\n" - ), - Version.V4_0_4, ImmutableMap.of( - "bad-codegen", "java.lang.NullPointerException\n", - "type-confusion-regression2", "java.lang.NullPointerException\n", - "type-confusion-regression3", "java.lang.NullPointerException\n", - "merge-blocks-regression", "java.lang.NullPointerException\n" - ) - ); + Version.V4_4_4, + ImmutableMap.of( + "bad-codegen", "java.lang.NullPointerException\n", + "type-confusion-regression2", "java.lang.NullPointerException\n", + "type-confusion-regression3", "java.lang.NullPointerException\n", + "merge-blocks-regression", "java.lang.NullPointerException\n"), + Version.V4_0_4, + ImmutableMap.of( + "bad-codegen", "java.lang.NullPointerException\n", + "type-confusion-regression2", "java.lang.NullPointerException\n", + "type-confusion-regression3", "java.lang.NullPointerException\n", + "merge-blocks-regression", "java.lang.NullPointerException\n"), + Version.V13_0_0, + ImmutableMap.of( + "bad-codegen", + StringUtils.lines( + "java.lang.NullPointerException: Attempt to read from field 'Test Test.a'" + + " on a null object reference in method 'Test TestObject.a(Test," + + " Test, Test, Test, boolean)'"), + "type-confusion-regression3", + StringUtils.lines( + "java.lang.NullPointerException: Attempt to read from field 'byte[]" + + " Test.a' on a null object reference in method 'int" + + " TestObject.a(Test, Test)'"))); // Tests where the input fails with a verification error on Dalvik instead of the // expected runtime exception. - private static Map<DexVm.Version, List<String>> dalvikVerificationError = ImmutableMap.of( - Version.V4_4_4, ImmutableList.of( - // The invokes are in fact invalid, but the test expects the current Art behavior - // of throwing an IncompatibleClassChange exception. Dalvik fails to verify. - "illegal-invokes" - ), - Version.V4_0_4, ImmutableList.of( - // The invokes are in fact invalid, but the test expects the current Art behavior - // of throwing an IncompatibleClassChange exception. Dalvik fails to verify. - "illegal-invokes" - ) - ); - - // Tests where the original smali code runs on Art, but fails after R8 processing - private static Map<String, List<String>> failingOnArtVersions = ImmutableMap.of( - // This list is currently empty! - ); + private static final Map<DexVm.Version, List<String>> dalvikVerificationErrors = + ImmutableMap.of( + Version.V4_4_4, + ImmutableList.of( + // The invokes are in fact invalid, but the test expects the current Art behavior + // of throwing an IncompatibleClassChange exception. Dalvik fails to verify. + "illegal-invokes"), + Version.V4_0_4, + ImmutableList.of( + // The invokes are in fact invalid, but the test expects the current Art behavior + // of throwing an IncompatibleClassChange exception. Dalvik fails to verify. + "illegal-invokes")); private Set<String> failingOnX8 = ImmutableSet.of( // Contains use of register as both an int and a float. @@ -110,59 +183,23 @@ @Rule public TestDescriptionWatcher watcher = new TestDescriptionWatcher(); - @Parameters(name = "{0}") - public static Collection<String[]> data() { - return Arrays.asList(new String[][]{ - {"arithmetic", - StringUtils.lines("-1", "3", "2", "3", "3.0", "1", "0", "-131580", "-131580", "2", "4", - "-2")}, - {"controlflow", - StringUtils.lines("2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2")}, - {"fibonacci", StringUtils.lines("55", "55", "55", "55")}, - {"fill-array-data", "[1, 2, 3][4, 5, 6]"}, - {"filled-new-array", "[1, 2, 3][4, 5, 6][1, 2, 3, 4, 5, 6][6, 5, 4, 3, 2, 1]"}, - {"packed-switch", "12345"}, - {"sparse-switch", "12345"}, - {"unreachable-code-1", "777"}, - {"multiple-returns", - StringUtils.lines("TFtf", "1", "4611686018427387904", "true", "false")}, - {"try-catch", ""}, - {"phi-removal-regression", StringUtils.lines("returnBoolean")}, - {"overlapping-long-registers", - StringUtils.lines("-9151314442816847872", "-9151314442816319488")}, - {"type-confusion-regression", - StringUtils.lines("java.lang.RuntimeException: Test.<init>()")}, - {"type-confusion-regression2", - StringUtils.lines("java.lang.NullPointerException: Attempt to read from null array")}, - {"type-confusion-regression3", - StringUtils.lines( - "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" + - " on a null object reference")}, - {"type-confusion-regression4", ""}, - {"type-confusion-regression5", StringUtils.lines("java.lang.RuntimeException: getId()I")}, - {"chain-of-loops", StringUtils.lines("java.lang.RuntimeException: f(II)")}, - {"new-instance-and-init", StringUtils.lines("Test(0)", "Test(0)", "Test(0)")}, - {"bad-codegen", - StringUtils.lines("java.lang.NullPointerException: Attempt to read from field " + - "'Test Test.a' on a null object reference")}, - {"merge-blocks-regression", - StringUtils.lines("java.lang.NullPointerException: Attempt to invoke virtual" - + " method 'Test Test.bW_()' on a null object reference")}, - {"self-is-catch-block", StringUtils.lines("100", "-1")}, - {"infinite-loop", ""}, - {"regression/33336471", - StringUtils.lines("START", "0", "2", "LOOP", "1", "2", "LOOP", "2", "2", "DONE", - "START", "0", "2", "LOOP", "1", "2", "LOOP", "2", "2", "DONE")}, - {"regression/33846227", ""}, - {"illegal-invokes", StringUtils.lines("ICCE", "ICCE")}, - }); - } + private final TestParameters parameters; + private final String directoryName; + private final String dexFileName; + private final String expectedOutput; - private String directoryName; - private String dexFileName; - private String expectedOutput; - - public R8RunSmaliTestsTest(String name, String expectedOutput) { + public R8RunSmaliTestsTest(TestParameters parameters, String name) { + this.parameters = parameters; + String expectedOutput = tests.get(name); + if (customProcessedOutputExpectation.containsKey(parameters.asDexRuntime().getVersion()) + && customProcessedOutputExpectation + .get(parameters.asDexRuntime().getVersion()) + .containsKey(name)) { + // If the original and the processed code have different expected output, only run + // the code produced by R8. + expectedOutput = + customProcessedOutputExpectation.get(parameters.asDexRuntime().getVersion()).get(name); + } this.directoryName = name; this.dexFileName = name.substring(name.lastIndexOf('/') + 1) + ".dex"; this.expectedOutput = expectedOutput; @@ -171,56 +208,40 @@ @Test public void SmaliTest() throws Exception { Path originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName); - Path outputPath = temp.getRoot().toPath().resolve("classes.dex"); + // Path outputPath = temp.getRoot().toPath().resolve("classes.dex"); if (failingOnX8.contains(directoryName)) { thrown.expect(CompilationFailedException.class); } - testForR8(Backend.DEX) + Version version = parameters.asDexRuntime().getVersion(); + boolean dalvikVerificationError = + dalvikVerificationErrors.containsKey(version) + && dalvikVerificationErrors.get(version).contains(directoryName); + boolean originalFailing = + (originalFailingOnArtVersions.containsKey(version) + && originalFailingOnArtVersions.get(version).contains(directoryName)); + testForR8(parameters.getBackend()) .addKeepAllClassesRule() .addProgramDexFileData(Files.readAllBytes(originalDexFile)) .addDontWarn(missingClasses.getOrDefault(directoryName, Collections.emptySet())) + .setMinApi(parameters.getApiLevel()) .compile() - .writeToZip(outputPath); + .run(parameters.getRuntime(), "Test") + .applyIf( + dalvikVerificationError, + r -> r.assertFailureWithErrorThatThrows(VerifyError.class), + r -> r.assertSuccessWithOutput(expectedOutput)); - if (!ToolHelper.artSupported()) { - return; + // Also run the original DEX if possible. + if (!dalvikVerificationError && !originalFailing) { + String originalOutput = + ToolHelper.runArtNoVerificationErrors( + ImmutableList.of(originalDexFile.toString()), + "Test", + null, + parameters.getRuntime().asDex().getVm()); + assertEquals(expectedOutput, originalOutput); } - - String mainClass = "Test"; - String generated = outputPath.toString(); - String output = ""; - - DexVm.Version dexVmVersion = ToolHelper.getDexVm().getVersion(); - if (dalvikVerificationError.containsKey(dexVmVersion) - && dalvikVerificationError.get(dexVmVersion).contains(directoryName)) { - try { - ToolHelper.runArtNoVerificationErrors(generated, mainClass); - } catch (AssertionError e) { - assert e.toString().contains("VerifyError"); - } - return; - } else if (originalFailingOnArtVersions.containsKey(dexVmVersion) - && originalFailingOnArtVersions.get(dexVmVersion).contains(directoryName)) { - // If the original smali code fails on the target VM, only run the code produced by R8. - output = ToolHelper.runArtNoVerificationErrors(generated, mainClass); - } else if (customProcessedOutputExpectation.containsKey(dexVmVersion) - && customProcessedOutputExpectation.get(dexVmVersion).containsKey(directoryName)) { - // If the original and the processed code have different expected output, only run - // the code produced by R8. - expectedOutput = - customProcessedOutputExpectation.get(dexVmVersion).get(directoryName); - output = ToolHelper.runArtNoVerificationErrors(generated, mainClass); - } else { - if (failingOnArtVersions.containsKey(dexVmVersion) - && failingOnArtVersions.get(dexVmVersion).contains(directoryName)) { - thrown.expect(Throwable.class); - } - output = - ToolHelper - .checkArtOutputIdentical(originalDexFile.toString(), generated, mainClass, null); - } - assertEquals(expectedOutput, output); } }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java index e6cd425..7fe1ae0 100644 --- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -310,7 +310,7 @@ // TODO(b/120402963): Triage. "invokecustom", "invokecustom2")) .put( - Version.V13_MASTER, + Version.V13_0_0, ImmutableList.of( // TODO(b/120402963): Triage. "invokecustom", "invokecustom2"))
diff --git a/src/test/java/com/android/tools/r8/SanityCheck.java b/src/test/java/com/android/tools/r8/SanityCheck.java index df1486c..c461475 100644 --- a/src/test/java/com/android/tools/r8/SanityCheck.java +++ b/src/test/java/com/android/tools/r8/SanityCheck.java
@@ -117,7 +117,6 @@ @Test public void testJarsContent() throws Exception { - checkJarContent(ToolHelper.D8_JAR); checkJarContent(ToolHelper.R8_JAR); } }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index 21ca89e..097e4bc 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -785,7 +785,7 @@ return computeAppViewWithClassHierarchy(app, keepConfig, null); } - private static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierarchy( + protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierarchy( AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig, Consumer<InternalOptions> optionsConsumer)
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java index 719dbd8..48f9fc5 100644 --- a/src/test/java/com/android/tools/r8/TestCompileResult.java +++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -331,6 +331,15 @@ } } + public CR addBootClasspathFiles(Path... files) { + return addBootClasspathFiles(Arrays.asList(files)); + } + + public CR addBootClasspathFiles(Collection<Path> files) { + additionalBootClasspath.addAll(files); + return self(); + } + public CR addDesugaredCoreLibraryRunClassPath( Function<AndroidApiLevel, Path> classPathSupplier, AndroidApiLevel minAPILevel) { addRunClasspathFiles(classPathSupplier.apply(minAPILevel));
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java index cd0a1e7..b1a6207 100644 --- a/src/test/java/com/android/tools/r8/TestCondition.java +++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -53,7 +53,7 @@ return ART_V10_0_0; case V12_0_0: return ART_V12_0_0; - case V13_MASTER: + case V13_0_0: return ART_V13_0_0; case DEFAULT: return ART_DEFAULT;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 7bcdfb6..f219969 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -161,7 +161,6 @@ public static final long BOT_MAX_HEAP_SIZE = 7908360192L; - public static final Path D8_JAR = Paths.get(LIBS_DIR, "d8.jar"); public static final Path R8_JAR = Paths.get(LIBS_DIR, "r8.jar"); public static final Path R8_WITH_DEPS_JAR = Paths.get(LIBS_DIR, "r8_with_deps.jar"); public static final Path R8_WITHOUT_DEPS_JAR = @@ -204,12 +203,16 @@ return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs.json"); } + public static Path getDesugarLibJsonForTestingWithPath() { + return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_path.json"); + } + public static Path getCHMOnlyDesugarLibJsonForTesting() { return Paths.get(getDesugarLibraryJsonDir(), "chm_only_desugar_jdk_libs.json"); } public static Path getDesugarLibJsonForTestingAlternative3() { - return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_alternative_3.json"); + return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_path_alternative_3.json"); } public static boolean isLocalDevelopment() { @@ -271,8 +274,8 @@ ART_10_0_0_HOST(Version.V10_0_0, Kind.HOST), ART_12_0_0_TARGET(Version.V12_0_0, Kind.TARGET), ART_12_0_0_HOST(Version.V12_0_0, Kind.HOST), - ART_13_0_0_TARGET(Version.V13_MASTER, Kind.TARGET), - ART_13_0_0_HOST(Version.V13_MASTER, Kind.HOST); + ART_13_0_0_TARGET(Version.V13_0_0, Kind.TARGET), + ART_13_0_0_HOST(Version.V13_0_0, Kind.HOST); private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP = Arrays.stream(DexVm.values()).collect(ImmutableMap.toImmutableMap( @@ -290,7 +293,7 @@ V9_0_0("9.0.0"), V10_0_0("10.0.0"), V12_0_0("12.0.0"), - V13_MASTER("13.0.0"); + V13_0_0("13.0.0"); /** This should generally be the latest DEX VM fully supported. */ // TODO(b/204855476): Rename to DEFAULT alias once the checked in VM is removed. @@ -352,7 +355,7 @@ } public static Version last() { - return V13_MASTER; + return V13_0_0; } static { @@ -623,7 +626,7 @@ private static final Map<DexVm, String> ART_DIRS = ImmutableMap.<DexVm, String>builder() .put(DexVm.ART_DEFAULT, "art") - .put(DexVm.ART_13_0_0_HOST, "host/art-13-master") + .put(DexVm.ART_13_0_0_HOST, "host/art-13-dev") .put(DexVm.ART_12_0_0_HOST, "host/art-12.0.0-beta4") .put(DexVm.ART_10_0_0_HOST, "art-10.0.0") .put(DexVm.ART_9_0_0_HOST, "art-9.0.0") @@ -673,14 +676,21 @@ "core-oj-hostdex.jar", "apache-xml-hostdex.jar"); + private static final List<String> NEWER_ART_BOOT_LIBS = + ImmutableList.of( + "core-libart-hostdex.jar", + "core-oj-hostdex.jar", + "core-icu4j-hostdex.jar", + "apache-xml-hostdex.jar"); + private static final Map<DexVm, List<String>> BOOT_LIBS; static { ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder(); builder .put(DexVm.ART_DEFAULT, ART_BOOT_LIBS) - .put(DexVm.ART_13_0_0_HOST, ART_BOOT_LIBS) - .put(DexVm.ART_12_0_0_HOST, ART_BOOT_LIBS) + .put(DexVm.ART_13_0_0_HOST, NEWER_ART_BOOT_LIBS) + .put(DexVm.ART_12_0_0_HOST, NEWER_ART_BOOT_LIBS) .put(DexVm.ART_10_0_0_HOST, ART_BOOT_LIBS) .put(DexVm.ART_9_0_0_HOST, ART_BOOT_LIBS) .put(DexVm.ART_8_1_0_HOST, ART_BOOT_LIBS) @@ -1027,7 +1037,7 @@ public static AndroidApiLevel getMinApiLevelForDexVm(DexVm dexVm) { switch (dexVm.version) { - case V13_MASTER: + case V13_0_0: return AndroidApiLevel.T; case V12_0_0: return AndroidApiLevel.S; @@ -1996,7 +2006,7 @@ "b/144975341", vm.version == DexVm.Version.V10_0_0 || vm.version == DexVm.Version.V12_0_0 - || vm.version == DexVm.Version.V13_MASTER); + || vm.version == DexVm.Version.V13_0_0); if (vm.isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) { // Run default dex2oat for tests on dalvik runtimes. vm = DexVm.ART_DEFAULT;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java index 784fce6..61a9e78 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -9,7 +9,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -58,10 +57,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -74,10 +70,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) // TODO(b/213552119): Remove when enabled by default. @@ -92,10 +85,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java index 8651a23..ec4a09e 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -7,7 +7,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -16,7 +15,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; import org.junit.Test; @@ -54,10 +52,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -70,10 +65,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) // TODO(b/213552119): Remove when enabled by default. @@ -88,10 +80,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java index 1397da7..b999875 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -7,7 +7,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -17,7 +16,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -62,10 +60,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -78,10 +73,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) // TODO(b/213552119): Remove when enabled by default. @@ -96,10 +88,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java index 3d6d30c..78c5fc1 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -7,7 +7,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -16,7 +15,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -61,10 +59,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -77,10 +72,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) // TODO(b/213552119): Remove when enabled by default. @@ -95,10 +87,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java index c2a6d4d..d6911db 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -7,7 +7,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -16,7 +15,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -71,10 +69,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -92,10 +87,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) // TODO(b/213552119): Remove when enabled by default. @@ -115,10 +107,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java index 85429ea..a3183b0 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -11,7 +11,6 @@ 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.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.NeverInline; @@ -21,7 +20,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; @@ -67,10 +65,7 @@ @Test public void testD8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); String result; if (parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel)) { @@ -90,10 +85,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java index e8d6257..57eaa67 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -13,7 +13,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -23,7 +22,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -77,10 +75,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -93,9 +88,7 @@ @Test public void testD8Release() throws Exception { - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -108,9 +101,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isCfRuntime() || parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java index fb98821..9793b9b 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -10,7 +10,6 @@ import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -20,7 +19,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -90,10 +88,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -120,10 +115,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -138,10 +130,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java index 2411e94..0b2f270 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -8,7 +8,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -17,7 +16,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.apimodel.ApiModelMockClassTest.TestClass; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; @@ -69,10 +67,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -85,10 +80,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -101,10 +93,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java index cf440c9..0fbd97c 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -13,7 +13,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -23,7 +22,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -87,10 +85,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -103,9 +98,7 @@ @Test public void testD8Release() throws Exception { - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8(parameters.getBackend()) .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -118,10 +111,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java index d63e01b..39b1db2 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
@@ -7,7 +7,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -19,7 +18,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import java.lang.reflect.Method; @@ -93,10 +91,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -108,10 +103,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -123,10 +115,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java index 940864c..fdd796b 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -5,7 +5,6 @@ package com.android.tools.r8.apimodel; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -14,7 +13,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import org.junit.Test; @@ -56,10 +54,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -73,10 +68,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isCfRuntime() - || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.RELEASE) .apply(this::setupTestBuilder) @@ -90,10 +82,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java index 94ae936..abf4efd 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; @@ -16,7 +15,6 @@ import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.testing.AndroidBuildVersion; import com.android.tools.r8.utils.AndroidApiLevel; import org.junit.Test; @@ -45,9 +43,7 @@ @Test public void testD8BootClassPath() throws Exception { - // TODO(b/197078995): Make this work on 12+. assumeTrue(parameters.isDexRuntime()); - assumeTrue(parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); compileOnD8() .addBootClasspathClasses(LibraryClass.class) .run(parameters.getRuntime(), Main.class) @@ -92,10 +88,7 @@ @Test public void testD8Debug() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .setMode(CompilationMode.DEBUG) .apply(this::setupTestBuilder) @@ -109,10 +102,7 @@ @Test public void testD8Release() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeTrue( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)); + assumeTrue(parameters.isDexRuntime()); testForD8() .apply(this::setupTestBuilder) .compile() @@ -125,10 +115,6 @@ @Test public void testR8() throws Exception { - // TODO(b/197078995): Make this work on 12+. - assumeFalse( - parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0)); testForR8(parameters.getBackend()) .apply(this::setupTestBuilder) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java index e4a1a1a..57aaddc 100644 --- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -79,7 +79,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory()); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterfaceHolder(method); + appView.appInfo().resolveMethodOnInterfaceHolderLegacy(method); DexType typeI = buildType(I.class, appView.dexItemFactory()); DexType typeL = buildType(L.class, appView.dexItemFactory()); DexType typeA = buildType(A.class, appView.dexItemFactory()); @@ -121,7 +121,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory()); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterfaceHolder(method); + appView.appInfo().resolveMethodOnInterfaceHolderLegacy(method); DexType typeI = buildType(I.class, appView.dexItemFactory()); DexType typeL = buildType(L.class, appView.dexItemFactory()); DexType typeA = buildType(A.class, appView.dexItemFactory()); @@ -162,7 +162,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, J.class); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory()); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterfaceHolder(method); + appView.appInfo().resolveMethodOnInterfaceHolderLegacy(method); DexType typeI = buildType(I.class, appView.dexItemFactory()); DexType typeB = buildType(A.class, appView.dexItemFactory()); DexProgramClass classI = appView.definitionForProgramType(typeI); @@ -200,7 +200,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, A.class); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory()); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterfaceHolder(method); + appView.appInfo().resolveMethodOnInterfaceHolderLegacy(method); DexType typeI = buildType(I.class, appView.dexItemFactory()); DexType typeB = buildType(A.class, appView.dexItemFactory()); DexProgramClass classI = appView.definitionForProgramType(typeI); @@ -240,7 +240,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory()); MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethodOnInterfaceHolder(method); + appView.appInfo().resolveMethodOnInterfaceHolderLegacy(method); DexType typeI = buildType(I.class, appView.dexItemFactory()); DexType typeB = buildType(A.class, appView.dexItemFactory()); DexProgramClass classI = appView.definitionForProgramType(typeI);
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java index a5e76eb..f3a22bf 100644 --- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java +++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -5,11 +5,10 @@ package com.android.tools.r8.debuginfo; import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber; -import static com.android.tools.r8.utils.InternalOptions.LineNumberOptimization.ON; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.SourceFileEnvironment; @@ -74,7 +73,6 @@ .addProgramClasses(MAIN) .setMinApi(parameters.getApiLevel()) .internalEnableMappingOutput() - // TODO(b/191038746): Enable LineNumberOptimization for release builds for DEX PC Output. .applyIf( apiLevelSupportsPcAndSourceFileOutput(), builder -> @@ -92,7 +90,6 @@ return true; } }; - options.lineNumberOptimization = ON; })) .run(parameters.getRuntime(), MAIN) .inspectFailure( @@ -150,10 +147,7 @@ List<DexDebugEntry> entries = new DexDebugEntryBuilder(main.getMethod(), inspector.getFactory()).build(); Set<Integer> lines = entries.stream().map(e -> e.line).collect(Collectors.toSet()); - // Check some of the lines in main are present (not 27 as it may be optimized out). - assertTrue(lines.contains(22)); - assertTrue(lines.contains(23)); - assertTrue(lines.contains(25)); + assertFalse(lines.isEmpty()); } private void checkExpectedStackTrace(StackTrace stackTrace) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java index d0db2c9..8eff1c5 100644 --- a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java +++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
@@ -102,7 +102,7 @@ .setMinApi(AndroidApiLevel.ANDROID_PLATFORM) .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class) .applyIf( - parameters.getDexRuntimeVersion().isOlderThan(Version.V13_MASTER), + parameters.getDexRuntimeVersion().isOlderThan(Version.V13_0_0), b -> b.assertFailureWithErrorThatMatches( containsString(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java index 93ea904..fbfcb18 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -7,16 +7,10 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.NeverInline; -import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; -import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils; -import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.StringUtils; -import com.google.common.collect.ImmutableList; import java.io.BufferedReader; import java.io.StringReader; import java.io.UncheckedIOException; @@ -54,28 +48,6 @@ return StringUtils.lines("Hello", "Larry", "Page", "Caught java.io.UncheckedIOException"); } - DesugaredLibrarySpecification configurationAlternative3( - InternalOptions options, boolean libraryCompilation, TestParameters parameters) { - // Parse the current configuration and amend the configuration for BufferedReader.lines. The - // configuration is the same for both program and library. - return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification( - StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingAlternative3()), - options.dexItemFactory(), - options.reporter, - libraryCompilation, - parameters.getApiLevel().getLevel()); - } - - private void configurationForProgramCompilation(InternalOptions options) { - setDesugaredLibrarySpecificationForTesting( - options, configurationAlternative3(options, false, parameters)); - } - - private void configurationForLibraryCompilation(InternalOptions options) { - setDesugaredLibrarySpecificationForTesting( - options, configurationAlternative3(options, true, parameters)); - } - @Test public void testBufferedReaderD8Cf() throws Exception { Assume.assumeTrue( @@ -85,7 +57,6 @@ // Use D8 to desugar with Java classfile output. Path jar = testForD8(Backend.CF) - .addOptionsModification(this::configurationForProgramCompilation) .addInnerClasses(BufferedReaderTest.class) .setMinApi(parameters.getApiLevel()) .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) @@ -108,13 +79,7 @@ .disableDesugaring() .compile() .addDesugaredCoreLibraryRunClassPath( - (apiLevel, keepRules, shrink) -> - buildDesugaredLibrary( - apiLevel, - keepRules, - shrink, - ImmutableList.of(), - this::configurationForLibraryCompilation), + this::buildDesugaredLibrary, parameters.getApiLevel(), desugaredLibraryKeepRules, shrinkDesugaredLibrary) @@ -122,8 +87,7 @@ .assertSuccessWithOutput(expectedOutput()); } else { // Build the desugared library in class file format. - Path desugaredLib = - getDesugaredLibraryInCF(parameters, this::configurationForLibraryCompilation); + Path desugaredLib = getDesugaredLibraryInCF(parameters, opt -> {}); // Run on the JVM with desugared library on classpath. testForJvm() @@ -143,19 +107,12 @@ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForD8() .addLibraryFiles(getLibraryFile()) - .addOptionsModification(this::configurationForProgramCompilation) .addInnerClasses(BufferedReaderTest.class) .setMinApi(parameters.getApiLevel()) .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) .compile() .addDesugaredCoreLibraryRunClassPath( - (apiLevel, keepRules, shrink) -> - buildDesugaredLibrary( - apiLevel, - keepRules, - shrink, - ImmutableList.of(), - this::configurationForLibraryCompilation), + this::buildDesugaredLibrary, parameters.getApiLevel(), keepRuleConsumer.get(), shrinkDesugaredLibrary) @@ -172,7 +129,6 @@ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForR8(parameters.getBackend()) .addLibraryFiles(getLibraryFile()) - .addOptionsModification(this::configurationForProgramCompilation) .addInnerClasses(BufferedReaderTest.class) .addKeepMainRule(TestClass.class) .setMinApi(parameters.getApiLevel()) @@ -180,13 +136,7 @@ .enableInliningAnnotations() .compile() .addDesugaredCoreLibraryRunClassPath( - (apiLevel, keepRules, shrink) -> - buildDesugaredLibrary( - apiLevel, - keepRules, - shrink, - ImmutableList.of(), - this::configurationForLibraryCompilation), + this::buildDesugaredLibrary, parameters.getApiLevel(), keepRuleConsumer.get(), shrinkDesugaredLibrary)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java index 0874b57..dc9725a 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
@@ -35,6 +35,8 @@ .put("java/lang/DesugarMath", "java/lang/Math") .put("java/io/DesugarBufferedReader", "java/io/BufferedReader") .put("java/io/DesugarInputStream", "java/io/InputStream") + .put("wrapper/adapter/HybridFileSystemProvider", "java/adapter/HybridFileSystemProvider") + .put("wrapper/adapter/HybridFileTypeDetector", "java/adapter/HybridFileTypeDetector") .build(); public static void main(String[] args) throws Exception { @@ -47,9 +49,6 @@ return ToolHelper.getDesugarJDKLibsBazelGeneratedFile(); } Path desugaredLibJDK11Undesugared = Paths.get("build/libs/desugar_jdk_libs_11_undesugared.jar"); - if (Files.exists(desugaredLibJDK11Undesugared)) { - return desugaredLibJDK11Undesugared; - } return generateUndesugaredJar(desugaredLibJDK11Undesugared); }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileTypeDetectorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileTypeDetectorTest.java new file mode 100644 index 0000000..b168992 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FileTypeDetectorTest.java
@@ -0,0 +1,173 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.desugaredlibrary.jdk11; + +import static com.android.tools.r8.ToolHelper.DexVm.Version.V12_0_0; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.StringResource; +import com.android.tools.r8.TestParameters; +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.utils.BooleanUtils; +import com.android.tools.r8.utils.StringUtils; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.spi.FileTypeDetector; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FileTypeDetectorTest extends DesugaredLibraryTestBase { + + private static final String EXPECTED_RESULT_DEFAULT_PNG_TYPE_DETECTOR = + StringUtils.lines("false", "text/plain", "image/png", "null", "image/png"); + private static final String EXPECTED_RESULT_NO_DEFAULT_PNG_TYPE_DETECTOR = + StringUtils.lines("false", "text/plain", "null", "null", "image/png"); + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}") + public static List<Object[]> data() { + // Skip Android 4.4.4 due to missing libjavacrypto. + return buildParameters( + BooleanUtils.values(), + getTestParameters() + .withDexRuntime(Version.V4_0_4) + .withDexRuntimesStartingFromIncluding(Version.V5_1_1) + .withAllApiLevels() + .build()); + } + + public FileTypeDetectorTest(boolean shrinkDesugaredLibrary, TestParameters parameters) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + private boolean hasDefaultPNGTypeDetector() { + return parameters.getDexRuntimeVersion().compareTo(V12_0_0) < 0; + } + + private String getExpectedResult() { + return hasDefaultPNGTypeDetector() + ? EXPECTED_RESULT_DEFAULT_PNG_TYPE_DETECTOR + : EXPECTED_RESULT_NO_DEFAULT_PNG_TYPE_DETECTOR; + } + + private LibraryDesugaringTestConfiguration pathConfiguration() { + return LibraryDesugaringTestConfiguration.builder() + .setMinApi(parameters.getApiLevel()) + .addDesugaredLibraryConfiguration( + StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath())) + .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG) + .withKeepRuleConsumer() + .build(); + } + + @Test + public void testD8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForD8(parameters.getBackend()) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(getClass()) + .addProgramClasses(GoogleIcon.class) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .withArt6Plus64BitsLib() + .withArtFrameworks() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(getExpectedResult()); + } + + @Test + public void testR8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForR8(Backend.DEX) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(getClass()) + .addProgramClasses(GoogleIcon.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .withArt6Plus64BitsLib() + .withArtFrameworks() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(getExpectedResult()); + } + + public static class TestClass { + + public static void main(String[] args) throws Throwable { + // FileTypeDetector usage through ServiceLoader. + ServiceLoader<FileTypeDetector> serviceLoader = ServiceLoader.load(FileTypeDetector.class); + Iterator<FileTypeDetector> iterator = serviceLoader.iterator(); + System.out.println(iterator.hasNext()); + while (iterator.hasNext()) { + FileTypeDetector fileTypeDetector = iterator.next(); + System.out.println((fileTypeDetector == null)); + } + + Path emptyText = Files.createTempFile("example", ".txt"); + Path png = getGoogleIconPng(); + + // FileTypeDetector usage through Files. + System.out.println(Files.probeContentType(emptyText)); + System.out.println(Files.probeContentType(png)); + + // Custom file type detector usage. + FileTypeDetector fileTypeDetector = new PngFileTypeDetector(); + System.out.println(fileTypeDetector.probeContentType(emptyText)); + System.out.println(fileTypeDetector.probeContentType(png)); + } + + private static Path getGoogleIconPng() throws IOException { + Path picture = Files.createTempFile("art", ".png"); + Files.write(picture, GoogleIcon.GOOGLE_ICON_PNG); + return picture; + } + } + + public static class PngFileTypeDetector extends FileTypeDetector { + + private static final byte[] PNG_HEADER = { + (byte) 0x89, + (byte) 0x50, + (byte) 0x4E, + (byte) 0x47, + (byte) 0x0D, + (byte) 0x0A, + (byte) 0x1A, + (byte) 0x0A + }; + + private static final int PNG_HEADER_SIZE = PNG_HEADER.length; + + @Override + public String probeContentType(final Path path) throws IOException { + final byte[] buf = new byte[PNG_HEADER_SIZE]; + + try (final InputStream in = Files.newInputStream(path); ) { + if (in.read(buf) != PNG_HEADER_SIZE) { + return null; + } + } + + return Arrays.equals(buf, PNG_HEADER) ? "image/png" : null; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java new file mode 100644 index 0000000..7b78157 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -0,0 +1,177 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.desugaredlibrary.jdk11; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.StringResource; +import com.android.tools.r8.TestParameters; +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.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.StringUtils; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.util.List; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FilesTest extends DesugaredLibraryTestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "bytes written: 11", + "String written: Hello World", + "bytes read: 11", + "String read: Hello World", + "null", + "true", + "unsupported"); + private static final String EXPECTED_RESULT_26 = + StringUtils.lines( + "bytes written: 11", + "String written: Hello World", + "bytes read: 11", + "String read: Hello World", + "true", + "true", + "true"); + + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}") + public static List<Object[]> data() { + // Skip Android 4.4.4 due to missing libjavacrypto. + return buildParameters( + BooleanUtils.values(), + getTestParameters() + .withDexRuntime(Version.V4_0_4) + .withDexRuntimesStartingFromIncluding(Version.V5_1_1) + .withAllApiLevels() + .build()); + } + + private String getExpectedResult() { + return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O) + ? EXPECTED_RESULT_26 + : EXPECTED_RESULT; + } + + public FilesTest(boolean shrinkDesugaredLibrary, TestParameters parameters) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + private LibraryDesugaringTestConfiguration pathConfiguration() { + return LibraryDesugaringTestConfiguration.builder() + .setMinApi(parameters.getApiLevel()) + .addDesugaredLibraryConfiguration( + StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath())) + .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG) + .withKeepRuleConsumer() + .build(); + } + + @Test + public void testD8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForD8(parameters.getBackend()) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(getClass()) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .withArt6Plus64BitsLib() + .withArtFrameworks() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(getExpectedResult()); + } + + @Test + public void testR8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForR8(Backend.DEX) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(getClass()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .withArt6Plus64BitsLib() + .withArtFrameworks() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(getExpectedResult()); + } + + public static class TestClass { + + public static void main(String[] args) throws Throwable { + Path path = Files.createTempFile("example", ".txt"); + readWrite(path); + + PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class); + if (view != null) { + System.out.println( + view.readAttributes().permissions().contains(PosixFilePermission.OWNER_READ)); + } else { + System.out.println("null"); + } + + BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class); + if (attributes != null) { + System.out.println(attributes.isRegularFile()); + } else { + System.out.println("null"); + } + + try { + PosixFileAttributes posixAttributes = Files.readAttributes(path, PosixFileAttributes.class); + if (attributes != null) { + System.out.println( + posixAttributes.permissions().contains(PosixFilePermission.OWNER_READ)); + } else { + System.out.println("null"); + } + } catch (UnsupportedOperationException e) { + System.out.println("unsupported"); + } + } + + private static void readWrite(Path path) throws IOException { + try (SeekableByteChannel channel = + Files.newByteChannel(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { + String toWrite = "Hello World"; + + // Write the String toWrite into the channel. + ByteBuffer byteBuffer = ByteBuffer.wrap(toWrite.getBytes()); + int write = channel.write(byteBuffer); + System.out.println("bytes written: " + write); + System.out.println("String written: " + toWrite); + + // Read the String toWrite from the channel. + channel.position(0); + ByteBuffer byteBuffer2 = ByteBuffer.allocate(write); + int read = channel.read(byteBuffer2); + System.out.println("bytes read: " + read); + System.out.println("String read: " + new String(byteBuffer2.array())); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/GoogleIcon.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/GoogleIcon.java new file mode 100644 index 0000000..ce0be4d --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/GoogleIcon.java
@@ -0,0 +1,168 @@ +// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.desugaredlibrary.jdk11; + +public class GoogleIcon { + static final byte[] GOOGLE_ICON_PNG = + new byte[] { + -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 1, 0, 0, 0, 1, 0, 8, 3, + 0, 0, 0, 107, -84, 88, 84, 0, 0, 2, -21, 80, 76, 84, 69, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 10, 33, -74, 0, 0, + 0, -8, 116, 82, 78, 83, 0, 0, 14, -25, -109, -6, -3, 5, 1, 22, -4, 3, -34, 47, -2, -33, -27, + -13, 4, -5, 20, 16, 13, -19, -12, -18, 21, -10, -30, 18, 2, 7, -97, -99, -121, 42, 6, -114, + 35, -35, -123, 68, -65, -26, -119, -21, -29, 45, 8, -38, 59, -96, -56, -14, -17, 10, -43, + 108, -16, -85, -55, 28, -22, 52, -46, 115, 112, 93, 26, 17, -52, -58, 36, 75, 27, -103, -15, + -83, 23, -95, 84, -64, 78, -66, -105, 34, -127, -117, 99, -60, -101, 40, 79, -54, -91, 44, + -126, 89, -24, -128, -7, 15, -39, 70, -80, 33, -111, 24, -53, 88, 110, 60, -122, -87, 90, + 25, 9, -41, -59, -84, -108, -102, -75, 58, -31, 51, -61, 113, -20, 57, 122, -74, 29, -113, + 54, 87, -107, -104, -73, 74, 39, -48, -50, -32, 65, -86, -67, 83, -76, -70, -23, 114, 71, + -71, 119, -100, 11, 111, 127, -110, -37, 50, 73, 69, 86, -9, -11, 116, 66, -68, 19, 107, + -81, 63, -89, -124, 37, -79, 53, 56, -98, -62, 46, 38, 12, 64, 117, 106, 77, 32, 126, -49, + -69, -125, 123, -93, -92, 91, -106, 67, 62, -57, 95, 125, -82, -90, 97, 100, 121, 104, -44, + -77, -78, -36, 85, 30, 92, 82, 109, 102, -115, 103, 94, 41, -40, -47, -94, 105, 48, 124, + -63, 120, -8, 118, 43, 61, -72, -116, 31, -45, -51, 72, 76, -28, -42, -118, 55, 101, -29, + -118, -16, -44, 0, 0, 9, 41, 73, 68, 65, 84, 120, 94, -20, -64, -127, 0, 0, 0, 0, -61, -96, + -5, 83, -33, 96, 4, -75, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -100, -68, -69, -118, 110, -28, + -56, -30, 56, -68, -41, -79, 100, 102, 102, 26, -77, -57, 8, 51, -29, -79, 61, 100, 28, 102, + 102, 102, 102, 102, 102, 74, -122, 25, 2, 67, 73, 6, -126, -80, 97, 102, -50, 38, 89, 102, + -90, -1, -29, -66, -84, -114, -70, -69, 74, 114, 89, -3, -94, 43, 125, -49, 122, -47, -81, + 79, 67, -99, 62, 125, -117, 92, -107, -102, -42, 41, -42, 71, 89, 99, -17, 52, 15, 9, -48, + 105, -40, -115, 94, -101, 119, 118, 125, 116, 92, -25, -36, -28, -16, -121, 20, 77, -114, + 56, -80, -39, -121, 127, -128, -56, 49, -117, -77, 15, 31, -8, 29, 92, 112, 116, 59, 17, + -13, 0, -3, -113, -84, -22, 28, 5, -41, 84, 94, 36, -30, 29, 32, 102, -57, -118, -115, 112, + 85, -16, -121, 65, -60, 59, 64, 73, 118, 49, 92, 86, 51, -79, -111, -120, 117, -128, -14, + -82, 22, -72, -52, -102, -34, 66, -60, 58, -64, -70, 126, -48, -78, 86, -121, 23, 86, 91, + -95, -22, 88, 57, 17, -21, 0, 1, -66, -102, -93, 31, -11, 118, -34, -32, -84, -128, -40, + -40, -128, -84, -25, 111, 53, 7, 66, 42, -12, -77, 17, -66, 54, 43, 125, 7, 18, -15, 14, + -80, 56, 25, 54, 40, -52, 44, 33, -115, -112, -20, 56, -56, 116, -1, 3, 9, -72, 6, -120, + 126, 21, 54, -120, -97, 24, 64, 6, 95, -82, -76, 66, 34, 126, -96, -89, 4, 88, 80, 6, 27, + 20, 76, 32, -119, 103, 58, 67, 34, -12, 91, -49, 8, -16, -31, 21, -40, 96, -74, 31, 73, -91, + -84, -128, -52, 25, 79, 8, -16, 107, -40, -67, 75, 14, -51, -122, -52, 115, -4, 3, -28, -63, + -18, -49, -28, -60, 8, -56, -52, -29, 30, 96, 10, -20, 94, -10, 39, 103, 86, 65, -30, 74, + 57, -17, 0, 93, 96, -41, 119, 22, 57, 21, 121, 12, 18, -55, 75, 56, 7, 104, -75, -62, 110, + 51, -75, -95, 127, 50, 36, -74, -8, -13, 13, -48, -78, 17, 118, -123, 62, -44, -106, -4, 80, + 72, 124, -61, 54, -128, -33, 114, 104, -68, 66, 2, -75, 91, 65, -43, 44, -82, 1, -90, 64, + 35, 106, 12, -75, -51, 39, 12, 18, -117, -104, 6, 8, -103, 10, -115, 90, 82, -79, 3, 50, + 107, 88, 6, -16, 47, -128, 86, 30, -87, -16, 31, 5, -119, -35, 44, 3, 124, 1, -99, 61, -92, + -28, 77, 72, -124, 94, 96, 24, -96, -61, 39, -48, -102, 58, -116, -44, 12, -126, 68, 31, + -122, 1, 22, 67, 39, 121, 41, -87, 121, 30, 18, 29, 99, -40, 5, 72, -99, 11, -99, 113, -92, + 40, -78, 51, 36, -114, -80, 11, -16, 22, -12, -74, -112, -86, -121, 33, 49, -128, 93, -128, + 116, -24, -35, 36, 85, -67, -29, 33, -54, -19, -64, 44, 64, 76, 71, -24, 61, 77, -54, 102, + 66, 100, -51, 98, 22, 96, 44, 12, 78, -111, -78, 73, -112, -40, -63, 44, 64, 31, 24, -92, + -109, -78, -128, -66, 16, 77, -28, 21, 32, -88, -77, -8, 60, -81, 110, 55, 68, -29, 121, 5, + 24, 102, -127, -63, 9, 82, -41, 19, -94, 2, 94, 1, 6, -61, -88, -119, -44, -83, -125, 104, + 31, -81, 0, 31, -63, 104, -19, 82, 82, -74, -76, 24, -126, 50, 63, 86, 1, 50, 96, 116, -75, + -114, -44, 29, -123, 32, -84, -108, 85, -128, 63, 65, -80, -119, -44, 77, 100, 31, 96, 28, + 4, 31, -112, -70, 35, 16, -108, 69, 114, 10, -112, -38, 25, -126, -39, -92, 46, 36, 20, 70, + 47, 17, -89, 0, -111, -45, 33, 88, 79, -22, -94, -85, 97, 52, -124, 85, -128, -96, 101, 16, + 36, 60, 70, 106, -28, 1, 95, 99, 21, -128, -10, 65, -12, 38, -87, 91, 15, -93, -31, -68, 2, + 12, -126, 40, -113, -52, 44, 8, 47, -13, 10, -80, 8, -94, -71, -92, 110, 0, 12, 2, -57, -16, + 10, -112, 9, -111, -27, 62, 41, 27, 42, 62, 8, -14, 10, -48, 11, 18, -41, 72, -103, 47, 12, + 70, 19, -81, 0, -65, -126, -60, 92, 127, 82, -75, 19, 6, -83, -52, 2, 116, 42, -122, 40, + -76, 27, -87, 122, 26, 122, -59, -89, -103, 5, -96, -82, -112, 24, 74, -86, -6, 65, 47, + -109, -72, 5, 120, 7, 18, 9, 1, -92, 104, 20, 116, -110, 94, 96, 23, 96, 78, 4, 36, 54, + -109, -102, -76, 48, -24, -68, 65, -20, 2, -56, -49, -127, -62, 105, -92, 100, -42, 84, 104, + -43, -44, 49, 12, 112, 9, 50, 63, 37, 37, 99, -95, -77, -127, 24, 6, 8, -38, 15, -119, -124, + 6, 82, 113, 14, 90, -93, 34, 57, 6, -96, -53, -112, -87, 32, 5, 29, -50, 67, -93, -26, 62, + -79, 12, 16, 52, 14, 50, -113, 80, -37, 102, 64, -21, 11, -30, 25, -128, -98, -127, 76, 88, + 10, -75, 105, 11, 52, -34, 37, -82, 1, -24, 20, 100, 14, 81, 91, 14, 66, 99, 39, -15, 13, + -48, 24, -20, -46, -99, -96, -12, -25, -80, 75, 39, -58, 1, -24, 70, 20, 100, -34, 33, -89, + 86, -62, -18, -108, 63, -21, 0, 52, 31, 50, -127, -109, -56, -119, 30, -80, -37, -58, -2, + -117, -111, 108, -56, 88, 63, 38, -121, 78, -62, -18, 36, -79, 15, 64, -93, 33, -11, 93, 7, + -110, -118, -47, -84, -125, -29, 39, -111, 7, 4, -96, -95, -112, 90, -66, -99, 36, -58, 46, + -125, 13, 114, -97, 34, -113, 8, 64, -61, 33, 101, -15, -51, 33, -125, -21, -85, 97, 119, + -94, -127, 60, 36, 0, 45, 78, -128, 84, 85, -59, -116, 88, -78, -95, -100, -57, -105, 67, + 35, 59, -110, 60, 38, 0, 101, 21, -64, -127, -28, 39, 94, -100, -108, -65, 102, -63, 63, + 126, -71, -77, 54, 73, 120, 13, -30, 57, 1, 40, -75, 103, 61, -38, 101, 33, -15, 14, 32, + -38, 122, 46, 2, -22, 86, 17, -5, 0, -94, -37, 35, -62, -95, 104, -67, 31, -9, 0, 114, 41, + -33, 127, 90, 3, 5, 17, -9, -119, 113, 0, -25, 26, 6, -82, -84, -115, 67, 27, 110, 17, -37, + 0, 74, 18, -69, 13, -36, 80, 52, -6, -3, 21, 93, 95, -37, -107, 121, -19, -85, 93, 48, -118, + -24, -19, -63, 1, 68, 67, 96, -76, -101, -68, 41, -64, 83, 16, 12, -9, -86, 0, -25, 32, -8, + -42, -101, 2, -8, 77, -121, -32, -65, -34, 20, -32, 118, 20, 4, 103, -67, 41, -64, 35, 16, + -83, -10, -90, 0, 95, 65, 20, 81, -25, 69, 1, 70, 66, 98, -101, 23, 5, 120, 29, 18, -106, + 63, 122, -9, 53, 0, 40, 110, -15, -102, 0, 99, 2, 33, -13, 121, -106, -73, 4, 40, 13, -125, + -44, -28, 124, 47, 9, 64, -65, -128, -100, -27, -116, -105, 4, 104, -123, 35, 3, 98, -67, + 34, 64, -38, -113, 112, -28, 111, 37, -34, 16, -128, 70, -62, -95, -124, 121, -34, 16, -64, + 111, 63, 28, 43, 10, -14, -4, 0, 84, 110, -123, 99, 39, -22, 60, 63, 0, 45, -124, 19, -63, + -37, 61, 63, -128, -33, 93, 56, 81, 53, -48, -29, 3, 80, 98, 46, -100, -23, -31, -47, 1, -4, + -90, -11, -34, 27, -78, 16, 78, 101, 123, 96, -128, -46, -70, 110, 99, 31, -49, -84, -40, + 50, -82, 108, 114, 68, 32, -38, 50, 62, -51, -125, 2, 68, -66, 48, 99, -61, -24, -126, -36, + 8, -76, -57, -22, 7, -98, 17, 96, 90, -7, -61, -117, -106, 37, -63, 5, 67, 124, -8, 7, -104, + 48, -1, 78, 33, 92, 54, -28, 1, -17, 0, 23, -122, 55, 5, -62, -108, -43, 126, 124, 3, 44, + -23, -46, 28, 10, 7, 44, 113, -63, -5, 11, -18, -68, -97, -79, 106, 116, -6, -94, 71, -29, + -31, 68, 58, -41, 0, 33, 111, 108, -124, 68, 84, -14, 111, 50, 126, 59, 111, -63, -19, -24, + 82, -51, 79, -101, -31, -60, 71, 44, 3, 116, -69, 105, -127, -96, -2, -47, -123, 95, -113, + 57, 77, -94, -76, 34, 56, 22, 122, -119, 95, -128, 97, 25, 81, 48, -22, 62, 98, 70, 52, 57, + -76, -25, 0, 28, 42, -114, 102, 22, -96, -61, -17, 19, 96, 112, 124, -24, -126, 32, 114, + -86, -31, 61, 56, 84, -63, 43, -64, 83, -75, 48, -104, -34, -77, 63, -75, -19, -38, 84, 56, + 114, -112, 83, -128, -109, -58, -1, -79, -74, -53, 105, 82, -14, 86, 25, 28, -88, 76, 101, + 19, -96, 113, 5, -12, -84, -37, -94, 73, 85, -64, 19, 112, 96, 48, -105, 0, -61, -10, 67, + 111, -6, 117, 106, -113, 41, -112, 27, -60, 36, -64, -124, 3, -48, -5, 44, -111, -38, -25, + 114, 95, -56, 88, 67, 88, 4, -56, -6, 39, -12, -6, -92, 82, 123, 61, 25, 12, -103, 76, 14, + 1, -74, 6, 67, 111, 23, -71, -32, 47, 77, -112, -24, -50, 96, -81, -79, 78, -107, -48, -101, + -23, 79, -82, 72, 25, 4, 81, -32, 94, -9, 15, -112, 1, -67, -13, 49, -28, -102, -40, -61, + 16, -11, 114, -5, 0, -117, -95, 23, -6, 12, -71, 106, -38, -37, 16, 108, 115, -9, 0, 115, + -22, -123, -25, 87, -41, 45, -55, -123, -47, 110, 119, 15, -112, 1, -67, -92, 28, 50, -95, + 91, 18, 12, -2, -22, -17, -34, 1, 46, 4, 10, -61, 96, 77, -23, 2, -125, -16, 7, -18, 29, + -96, 31, 12, -14, -55, -100, 65, -48, -85, 14, 112, -21, 0, 89, 81, -48, 43, -18, 68, -26, + 116, 11, -124, 78, 124, -99, 91, 7, -8, 14, 6, 93, -55, -84, 39, -96, -109, -76, -43, -99, + 3, 44, 93, 11, -125, 103, -55, -84, 124, -24, 68, 52, -72, 115, -128, -21, -110, -11, -85, + 89, 105, -63, -48, -22, -40, -24, -50, 1, -118, 96, -12, 36, -103, -106, 1, -83, -49, -3, + -36, 57, -64, 40, 24, 88, -9, -110, 105, 35, -95, 53, -118, -36, 56, -128, -80, -87, 2, 44, + 57, 100, 90, 57, -76, 42, -36, 57, 64, 73, -88, 100, -15, 102, 90, 75, 20, 52, 122, -70, + 115, -128, -65, 67, -80, -114, 76, 91, 82, 5, -115, 5, -18, 28, -96, 23, 4, -83, 100, 90, + 98, 2, -20, -114, -5, -72, 115, -128, -97, 65, -80, -127, 76, -101, 83, 5, -69, 126, -60, + 44, -64, 120, 50, 45, -57, 2, -69, -41, -119, -39, 41, 16, -100, 70, 102, 109, -126, 93, 92, + -118, 91, 7, -72, 8, 81, 57, -103, -11, 61, -20, -6, -112, 91, 7, -72, 7, -47, 80, 50, -53, + 87, 120, 45, -64, -32, 65, 72, 55, 71, -43, 28, -1, 125, -80, -63, 76, 114, -17, 0, -76, 30, + -94, 91, 100, 78, 73, 32, 108, -84, -21, -36, 61, 64, -98, 124, -53, 88, 83, -118, -124, + -55, 114, 110, 28, 96, 13, 36, 62, 37, 51, 26, 39, -61, 38, -82, -63, -19, 3, -8, -3, 11, + 18, -13, -55, -124, -77, -80, -63, 72, 6, -13, 3, -98, -123, -124, 37, -97, 92, -42, 82, 5, + -101, -9, -120, 65, -128, -60, 56, 72, -60, 61, 73, 46, 74, 29, 2, -101, -32, 68, 14, 1, + -24, 69, -56, -44, 111, 34, -41, -4, 27, 54, 73, -1, 33, 22, 1, 124, -62, 32, -45, -9, 107, + 114, 69, 79, -19, 126, -117, 60, 2, -48, 88, -56, 77, 9, 34, 117, -30, 112, -51, 46, -60, + 37, 0, -19, -126, 92, 65, 8, -75, 79, -121, -39, -62, 103, 51, 44, 2, 44, -83, -123, 92, + -46, -62, 104, 106, -121, 123, 119, -123, -29, -49, 35, 0, 125, 25, 6, 7, -110, -97, 85, 78, + 16, 51, 37, 30, 54, 87, -25, 17, -85, 0, 52, 33, 28, -114, -124, 23, -107, -112, -126, -57, + 122, 6, -61, 6, -71, 107, -120, 89, 0, 42, 9, -125, 67, 81, -121, -97, 107, 33, -89, 82, + -73, 103, 107, 11, -34, 89, 66, -20, 2, -48, -84, 90, 56, 113, -91, 57, -77, 117, -85, 63, + 73, 5, -52, 40, 122, 9, 26, -15, 15, -13, -100, 31, 16, 91, 1, -25, -30, -69, 31, -102, -40, + -21, -58, 15, 115, 98, -3, -2, 127, -55, -9, -23, 29, -78, 103, 126, -97, -26, 56, -24, -68, + 124, -113, -19, -4, -128, -111, 27, -47, -74, -64, -66, -31, 101, -35, -101, 42, 43, -25, + 118, 63, -1, 80, 4, 4, 97, 31, 112, -98, 31, -112, 115, 8, -26, -44, -97, 77, 97, 62, 63, + -96, -75, 18, -82, 43, 124, -91, 55, -1, 17, 26, 65, -109, 92, 77, -80, -17, 76, -94, 103, + 12, 81, -15, -65, -40, -17, 42, -38, -21, -8, -128, 75, -111, 30, 52, 71, 40, -89, -57, 114, + 11, -44, -123, -33, -100, -105, -24, 113, -77, -60, 126, 120, 124, 102, 56, 20, 36, -36, + -51, -53, -113, 33, 1, -61, 0, 34, -97, 77, -33, 12, 104, -22, 8, 71, 66, -21, -101, 94, + -19, 113, -80, 63, 9, 56, 7, 16, 61, 22, 50, 120, -2, -120, -15, -57, -26, 46, 91, 59, 57, + -82, -70, -90, -90, -70, 99, 97, -16, 39, -51, 71, -45, -13, 62, -34, 51, 33, -123, 4, 28, + 3, 40, 10, -22, 52, 45, 58, 96, 78, -1, -128, -108, -40, 82, 127, 114, -55, -1, -38, -127, + 3, 1, 0, 0, 0, -122, 65, -9, -89, -66, -63, 8, 106, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + -31, -37, 22, 69, 99, -29, 53, -11, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126 + }; +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java index 77f16d2..bee154b 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
@@ -4,6 +4,9 @@ package com.android.tools.r8.desugar.desugaredlibrary.jdk11; +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; @@ -41,21 +44,24 @@ this.parameters = parameters; } + private LibraryDesugaringTestConfiguration pathConfiguration() { + return LibraryDesugaringTestConfiguration.builder() + .setMinApi(parameters.getApiLevel()) + .addDesugaredLibraryConfiguration( + StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath())) + .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG) + .withKeepRuleConsumer() + .build(); + } + @Test public void testD8() throws Exception { Assume.assumeTrue(isJDK11DesugaredLibrary()); - KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForD8(parameters.getBackend()) .addLibraryFiles(getLibraryFile()) .addProgramFiles(INPUT_JAR) .setMinApi(parameters.getApiLevel()) - .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) - .compile() - .addDesugaredCoreLibraryRunClassPath( - this::buildDesugaredLibrary, - parameters.getApiLevel(), - keepRuleConsumer.get(), - shrinkDesugaredLibrary) + .enableCoreLibraryDesugaring(pathConfiguration()) .run(parameters.getRuntime(), MAIN_CLASS) .assertSuccessWithOutput(EXPECTED_OUTPUT); } @@ -63,19 +69,12 @@ @Test public void testR8() throws Exception { Assume.assumeTrue(isJDK11DesugaredLibrary()); - KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForR8(parameters.getBackend()) .addLibraryFiles(getLibraryFile()) .addProgramFiles(INPUT_JAR) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_CLASS) - .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) - .compile() - .addDesugaredCoreLibraryRunClassPath( - this::buildDesugaredLibrary, - parameters.getApiLevel(), - keepRuleConsumer.get(), - shrinkDesugaredLibrary) + .enableCoreLibraryDesugaring(pathConfiguration()) .run(parameters.getRuntime(), MAIN_CLASS) .assertSuccessWithOutput(EXPECTED_OUTPUT); }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.java new file mode 100644 index 0000000..3065b0f --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathTest.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.jdk11; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.StringResource; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.StringUtils; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class PathTest extends DesugaredLibraryTestBase { + + private static final String EXPECTED_RESULT = StringUtils.lines("x.txt", "dir", "dir/x.txt", "/"); + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build()); + } + + public PathTest(boolean shrinkDesugaredLibrary, TestParameters parameters) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + private LibraryDesugaringTestConfiguration pathConfiguration() { + return LibraryDesugaringTestConfiguration.builder() + .setMinApi(parameters.getApiLevel()) + .addDesugaredLibraryConfiguration( + StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath())) + .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG) + .withKeepRuleConsumer() + .build(); + } + + @Test + public void testD8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForD8(parameters.getBackend()) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(PathTest.class) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + Assume.assumeTrue(isJDK11DesugaredLibrary()); + testForR8(Backend.DEX) + .addLibraryFiles(getLibraryFile()) + .addInnerClasses(PathTest.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .enableCoreLibraryDesugaring(pathConfiguration()) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + public static class TestClass { + + public static void main(String[] args) { + File file = new File("x.txt"); + Path path1 = file.toPath(); + System.out.println(path1); + Path path2 = Paths.get("dir/"); + System.out.println(path2); + Path resolve = path2.resolve(path1); + System.out.println(resolve); + System.out.println(resolve.getFileSystem().getSeparator()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java index 1b9581a..b55ebae 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java
@@ -4,7 +4,11 @@ package com.android.tools.r8.desugar.desugaredlibrary.jdk11; +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.LibraryDesugaringTestConfiguration; +import com.android.tools.r8.StringResource; import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; import com.android.tools.r8.transformers.MethodTransformer; import com.android.tools.r8.utils.BooleanUtils; @@ -42,21 +46,24 @@ this.parameters = parameters; } + private LibraryDesugaringTestConfiguration pathConfiguration() { + return LibraryDesugaringTestConfiguration.builder() + .setMinApi(parameters.getApiLevel()) + .addDesugaredLibraryConfiguration( + StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingWithPath())) + .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG) + .withKeepRuleConsumer() + .build(); + } + @Test public void testD8() throws Exception { Assume.assumeTrue(isJDK11DesugaredLibrary()); - KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForD8(parameters.getBackend()) .addLibraryFiles(getLibraryFile()) .addProgramClassFileData(getProgramClassFileData()) .setMinApi(parameters.getApiLevel()) - .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) - .compile() - .addDesugaredCoreLibraryRunClassPath( - this::buildDesugaredLibrary, - parameters.getApiLevel(), - keepRuleConsumer.get(), - shrinkDesugaredLibrary) + .enableCoreLibraryDesugaring(pathConfiguration()) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutput(EXPECTED_RESULT); } @@ -64,19 +71,12 @@ @Test public void testR8() throws Exception { Assume.assumeTrue(isJDK11DesugaredLibrary()); - KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); testForR8(Backend.DEX) .addLibraryFiles(getLibraryFile()) .addProgramClassFileData(getProgramClassFileData()) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(TestClass.class) - .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) - .compile() - .addDesugaredCoreLibraryRunClassPath( - this::buildDesugaredLibrary, - parameters.getApiLevel(), - keepRuleConsumer.get(), - shrinkDesugaredLibrary) + .enableCoreLibraryDesugaring(pathConfiguration()) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutput(EXPECTED_RESULT); }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java index f107b2a..ff80b66 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags; import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification; @@ -80,21 +81,18 @@ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec1, MultiAPILevelHumanDesugaredLibrarySpecification humanSpec2) { assertTopLevelFlagsEquals(humanSpec1.getTopLevelFlags(), humanSpec2.getTopLevelFlags()); - assertFlagMapEquals( - humanSpec1.getCommonFlagsForTesting(), humanSpec2.getCommonFlagsForTesting()); - assertFlagMapEquals( - humanSpec1.getLibraryFlagsForTesting(), humanSpec2.getLibraryFlagsForTesting()); - assertFlagMapEquals( - humanSpec1.getProgramFlagsForTesting(), humanSpec2.getProgramFlagsForTesting()); + assertFlagMapEquals(humanSpec1.getCommonFlags(), humanSpec2.getCommonFlags()); + assertFlagMapEquals(humanSpec1.getLibraryFlags(), humanSpec2.getLibraryFlags()); + assertFlagMapEquals(humanSpec1.getProgramFlags(), humanSpec2.getProgramFlags()); } private void assertFlagMapEquals( - Map<Integer, HumanRewritingFlags> commonFlags1, - Map<Integer, HumanRewritingFlags> commonFlags2) { + Map<ApiLevelRange, HumanRewritingFlags> commonFlags1, + Map<ApiLevelRange, HumanRewritingFlags> commonFlags2) { assertEquals(commonFlags1.size(), commonFlags2.size()); - for (int integer : commonFlags1.keySet()) { - assertTrue(commonFlags2.containsKey(integer)); - assertFlagsEquals(commonFlags1.get(integer), commonFlags2.get(integer)); + for (ApiLevelRange range : commonFlags1.keySet()) { + assertTrue(commonFlags2.containsKey(range)); + assertFlagsEquals(commonFlags1.get(range), commonFlags2.get(range)); } }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java index 5b700d5..ee0e845 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -138,7 +138,7 @@ } @Test(expected = CompilationFailedException.class) - @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V13_MASTER) // No desugaring + @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V13_0_0) // No desugaring public void testInvokeDefault1() throws Exception { ensureSameOutput( TestMainDefault1.class.getCanonicalName(),
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java index 45a3be4..71e7df5 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java
@@ -47,28 +47,6 @@ } @Test - public void testPropagationFromFeature() throws Exception { - ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone = - r8TestCompileResult -> { - // Ensure that getFromFeature from FeatureClass is inlined into the run method. - ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class); - assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent())); - }; - ProcessResult processResult = - testDexSplitter( - parameters, - ImmutableSet.of(BaseSuperClass.class), - ImmutableSet.of(FeatureClass.class), - FeatureClass.class, - EXPECTED, - ensureGetFromFeatureGone, - TestShrinkerBuilder::noMinification); - // We expect art to fail on this with the dex splitter, see b/122902374 - assertNotEquals(processResult.exitCode, 0); - assertTrue(processResult.stderr.contains("NoClassDefFoundError")); - } - - @Test public void testOnR8Splitter() throws IOException, CompilationFailedException { assumeTrue(parameters.isDexRuntime()); ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java index 96220f8..53af2b6 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
@@ -48,31 +48,6 @@ } @Test - public void testInliningFromFeature() throws Exception { - ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone = - r8TestCompileResult -> { - // Ensure that getFromFeature from FeatureClass is inlined into the run method. - ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class); - assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent())); - }; - Consumer<R8FullTestBuilder> configurator = - r8FullTestBuilder -> - r8FullTestBuilder.enableNoVerticalClassMergingAnnotations().noMinification(); - ProcessResult processResult = - testDexSplitter( - parameters, - ImmutableSet.of(BaseSuperClass.class), - ImmutableSet.of(FeatureClass.class), - FeatureClass.class, - EXPECTED, - ensureGetFromFeatureGone, - configurator); - // We expect art to fail on this with the dex splitter, see b/122902374 - assertNotEquals(processResult.exitCode, 0); - assertTrue(processResult.stderr.contains("NoClassDefFoundError")); - } - - @Test public void testOnR8Splitter() throws IOException, CompilationFailedException { assumeTrue(parameters.isDexRuntime()); ThrowableConsumer<R8FullTestBuilder> configurator =
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java index 38d19da..a2e1b1d 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
@@ -46,28 +46,6 @@ } @Test - public void testPropagationFromFeature() throws Exception { - ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone = - r8TestCompileResult -> { - // Ensure that getFromFeature from FeatureClass is inlined into the run method. - ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class); - assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent())); - }; - ProcessResult processResult = - testDexSplitter( - parameters, - ImmutableSet.of(BaseSuperClass.class), - ImmutableSet.of(FeatureClass.class, FeatureEnum.class), - FeatureClass.class, - EXPECTED, - ensureGetFromFeatureGone, - builder -> builder.enableInliningAnnotations().noMinification()); - // We expect art to fail on this with the dex splitter, see b/122902374 - assertNotEquals(processResult.exitCode, 0); - assertTrue(processResult.stderr.contains("NoClassDefFoundError")); - } - - @Test public void testOnR8Splitter() throws IOException, CompilationFailedException { assumeTrue(parameters.isDexRuntime()); ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java index 71ddab8..a7d137f 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -48,43 +48,6 @@ } @Test - public void testInliningFromFeature() throws Exception { - // Static merging is based on sorting order, we assert that we merged to the feature. - ThrowingConsumer<R8TestCompileResult, Exception> ensureMergingToFeature = - r8TestCompileResult -> { - ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class); - assertEquals(2, clazz.allMethods().size()); - assertThat(clazz.uniqueMethodWithName("getBase42"), isPresent()); - assertThat(clazz.uniqueMethodWithName("getFoobar"), isPresent()); - }; - Consumer<R8FullTestBuilder> configurator = - r8FullTestBuilder -> - r8FullTestBuilder - .addOptionsModification( - options -> - options.testing.horizontalClassMergingTarget = - (appView, candidates, target) -> candidates.iterator().next()) - .addHorizontallyMergedClassesInspector( - inspector -> - inspector.assertMergedInto(BaseWithStatic.class, AFeatureWithStatic.class)) - .enableNoVerticalClassMergingAnnotations() - .enableInliningAnnotations() - .noMinification(); - ProcessResult processResult = - testDexSplitter( - parameters, - ImmutableSet.of(BaseClass.class, BaseWithStatic.class), - ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class), - FeatureClass.class, - EXPECTED, - ensureMergingToFeature, - configurator); - // We expect art to fail on this with the dex splitter. - assertNotEquals(processResult.exitCode, 0); - assertTrue(processResult.stderr.contains("NoClassDefFoundError")); - } - - @Test public void testOnR8Splitter() throws IOException, CompilationFailedException { assumeTrue(parameters.isDexRuntime()); ThrowableConsumer<R8FullTestBuilder> configurator =
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java deleted file mode 100644 index e90e0ab..0000000 --- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java +++ /dev/null
@@ -1,484 +0,0 @@ -// Copyright (c) 2017, 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.dexsplitter; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.D8Command; -import com.android.tools.r8.DexSplitterHelper; -import com.android.tools.r8.ExtractMarker; -import com.android.tools.r8.OutputMode; -import com.android.tools.r8.R8; -import com.android.tools.r8.R8Command; -import com.android.tools.r8.ResourceException; -import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.ToolHelper.ArtCommandBuilder; -import com.android.tools.r8.dex.Marker; -import com.android.tools.r8.dexsplitter.DexSplitter.Options; -import com.android.tools.r8.origin.Origin; -import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException; -import com.android.tools.r8.utils.StringUtils; -import com.android.tools.r8.utils.codeinspector.ClassSubject; -import com.android.tools.r8.utils.codeinspector.CodeInspector; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class DexSplitterTests { - - private static final String CLASS_DIR = - ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR + "classes/dexsplitsample"; - private static final String CLASS1_CLASS = CLASS_DIR + "/Class1.class"; - private static final String CLASS2_CLASS = CLASS_DIR + "/Class2.class"; - private static final String CLASS3_CLASS = CLASS_DIR + "/Class3.class"; - private static final String CLASS3_INNER_CLASS = CLASS_DIR + "/Class3$InnerClass.class"; - private static final String CLASS3_SYNTHETIC_CLASS = CLASS_DIR + "/Class3$1.class"; - private static final String CLASS4_CLASS = CLASS_DIR + "/Class4.class"; - private static final String CLASS4_LAMBDA_INTERFACE = CLASS_DIR + "/Class4$LambdaInterface.class"; - private static final String TEXT_FILE = - ToolHelper.EXAMPLES_ANDROID_N_DIR + "dexsplitsample/TextFile.txt"; - - - @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); - - private Path createInput(boolean dontCreateMarkerInD8) - throws IOException, CompilationFailedException { - // Initial normal compile to create dex files. - Path inputZip = temp.newFolder().toPath().resolve("input.zip"); - D8Command command = - D8Command.builder() - .setOutput(inputZip, OutputMode.DexIndexed) - .addProgramFiles(Paths.get(CLASS1_CLASS)) - .addProgramFiles(Paths.get(CLASS2_CLASS)) - .addProgramFiles(Paths.get(CLASS3_CLASS)) - .addProgramFiles(Paths.get(CLASS3_INNER_CLASS)) - .addProgramFiles(Paths.get(CLASS3_SYNTHETIC_CLASS)) - .addProgramFiles(Paths.get(CLASS4_CLASS)) - .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE)) - .build(); - - DexSplitterHelper.runD8ForTesting(command, dontCreateMarkerInD8); - - return inputZip; - } - - private void testMarker(boolean addMarkerToInput) - throws CompilationFailedException, IOException, ResourceException, ExecutionException { - Path inputZip = createInput(!addMarkerToInput); - - Path output = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(output); - Path splitSpec = createSplitSpec(); - - DexSplitter.main( - new String[] { - "--input", inputZip.toString(), - "--output", output.toString(), - "--feature-splits", splitSpec.toString() - }); - - Path base = output.resolve("base").resolve("classes.dex"); - Path feature = output.resolve("feature1").resolve("classes.dex"); - - for (Path path : new Path[] {inputZip, base, feature}) { - Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(path); - assertEquals(addMarkerToInput ? 1 : 0, markers.size()); - } - } - - @Test - public void testMarkerPreserved() - throws CompilationFailedException, IOException, ResourceException, ExecutionException { - testMarker(true); - } - - @Test - public void testMarkerNotAdded() - throws CompilationFailedException, IOException, ResourceException, ExecutionException { - testMarker(false); - } - - /** - * To test the file splitting we have 3 classes that we distribute like this: Class1 -> base - * Class2 -> feature1 Class3 -> feature1 - * - * <p>Class1 and Class2 works independently of each other, but Class3 extends Class1, and - * therefore can't run without the base being loaded. - */ - @Test - public void splitFilesNoObfuscation() - throws CompilationFailedException, IOException, FeatureMappingException { - noObfuscation(false); - noObfuscation(true); - } - - private void noObfuscation(boolean useOptions) - throws IOException, CompilationFailedException, FeatureMappingException { - Path inputZip = createInput(false); - Path output = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(output); - Path splitSpec = createSplitSpec(); - - if (useOptions) { - Options options = new Options(); - options.addInputArchive(inputZip.toString()); - options.setFeatureSplitMapping(splitSpec.toString()); - options.setOutput(output.toString()); - DexSplitter.run(options); - } else { - DexSplitter.main( - new String[] { - "--input", inputZip.toString(), - "--output", output.toString(), - "--feature-splits", splitSpec.toString() - }); - } - - Path base = output.resolve("base").resolve("classes.dex"); - Path feature = output.resolve("feature1").resolve("classes.dex"); - validateUnobfuscatedOutput(base, feature); - } - - private void validateUnobfuscatedOutput(Path base, Path feature) throws IOException { - // Both classes should still work if we give all dex files to the system. - for (String className : new String[] {"Class1", "Class2", "Class3"}) { - ArtCommandBuilder builder = new ArtCommandBuilder(); - builder.appendClasspath(base.toString()); - builder.appendClasspath(feature.toString()); - builder.setMainClass("dexsplitsample." + className); - String out = ToolHelper.runArt(builder); - assertEquals(out, className + "\n"); - } - // Individual classes should also work from the individual files. - String className = "Class1"; - ArtCommandBuilder builder = new ArtCommandBuilder(); - builder.appendClasspath(base.toString()); - builder.setMainClass("dexsplitsample." + className); - String out = ToolHelper.runArt(builder); - assertEquals(out, className + "\n"); - - className = "Class2"; - builder = new ArtCommandBuilder(); - builder.appendClasspath(feature.toString()); - builder.setMainClass("dexsplitsample." + className); - out = ToolHelper.runArt(builder); - assertEquals(out, className + "\n"); - - className = "Class3"; - builder = new ArtCommandBuilder(); - builder.appendClasspath(feature.toString()); - builder.setMainClass("dexsplitsample." + className); - try { - ToolHelper.runArt(builder); - assertFalse(true); - } catch (AssertionError assertionError) { - // We expect this to throw since base is not in the path and Class3 depends on Class1 - } - - className = "Class4"; - builder = new ArtCommandBuilder(); - builder.appendClasspath(feature.toString()); - builder.setMainClass("dexsplitsample." + className); - try { - ToolHelper.runArt(builder); - assertFalse(true); - } catch (AssertionError assertionError) { - // We expect this to throw since base is not in the path and Class4 includes a lambda that - // would have been pushed to base. - } - } - - private Path createSplitSpec() throws FileNotFoundException, UnsupportedEncodingException { - Path splitSpec = temp.getRoot().toPath().resolve("split_spec"); - try (PrintWriter out = new PrintWriter(splitSpec.toFile(), "UTF-8")) { - out.write( - "dexsplitsample.Class1:base\n" - + "dexsplitsample.Class2:feature1\n" - + "dexsplitsample.Class3:feature1\n" - + "dexsplitsample.Class4:feature1"); - } - return splitSpec; - } - - private List<String> getProguardConf() { - return ImmutableList.of( - "-keep class dexsplitsample.Class3 {", - " public static void main(java.lang.String[]);", - "}"); - } - - @Test - public void splitFilesFromJar() - throws IOException, CompilationFailedException, FeatureMappingException { - for (boolean useOptions : new boolean[]{false, true}) { - for (boolean explicitBase: new boolean[]{false, true}) { - for (boolean renameBase: new boolean[]{false, true}) { - splitFromJars(useOptions, explicitBase, renameBase); - } - } - } - } - - private void splitFromJars(boolean useOptions, boolean explicitBase, boolean renameBase) - throws IOException, CompilationFailedException, FeatureMappingException { - Path inputZip = createInput(false); - Path output = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(output); - Path baseJar = temp.getRoot().toPath().resolve("base.jar"); - Path featureJar = temp.getRoot().toPath().resolve("feature1.jar"); - ZipOutputStream baseStream = new ZipOutputStream(Files.newOutputStream(baseJar)); - String name = "dexsplitsample/Class1.class"; - baseStream.putNextEntry(new ZipEntry(name)); - baseStream.write(Files.readAllBytes(Paths.get(CLASS1_CLASS))); - baseStream.closeEntry(); - baseStream.close(); - - ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar)); - name = "dexsplitsample/Class2.class"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS2_CLASS))); - featureStream.closeEntry(); - name = "dexsplitsample/Class3.class"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS3_CLASS))); - featureStream.closeEntry(); - name = "dexsplitsample/Class3$InnerClass.class"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS3_INNER_CLASS))); - featureStream.closeEntry(); - name = "dexsplitsample/Class3$1.class"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS3_SYNTHETIC_CLASS))); - featureStream.closeEntry(); - name = "dexsplitsample/Class4"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS4_CLASS))); - featureStream.closeEntry(); - name = "dexsplitsample/Class4$LambdaInterface"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(Files.readAllBytes(Paths.get(CLASS4_LAMBDA_INTERFACE))); - featureStream.closeEntry(); - featureStream.close(); - // Make sure that we can pass in a name for the output. - String specificOutputName = "renamed"; - if (useOptions) { - Options options = new Options(); - options.addInputArchive(inputZip.toString()); - options.setOutput(output.toString()); - if (explicitBase) { - options.addBaseJar(baseJar.toString()); - } else if (renameBase){ - // Ensure that we can rename base (if people called a feature base) - options.setBaseOutputName("base_renamed"); - } - options.addFeatureJar(featureJar.toString(), specificOutputName); - DexSplitter.run(options); - } else { - List<String> args = Lists.newArrayList( - "--input", - inputZip.toString(), - "--output", - output.toString(), - "--feature-jar", - featureJar.toString().concat(":").concat(specificOutputName)); - if (explicitBase) { - args.add("--base-jar"); - args.add(baseJar.toString()); - } else if (renameBase) { - args.add("--base-output-name"); - args.add("base_renamed"); - } - - DexSplitter.main(args.toArray(StringUtils.EMPTY_ARRAY)); - } - String baseOutputName = explicitBase || !renameBase ? "base" : "base_renamed"; - Path base = output.resolve(baseOutputName).resolve("classes.dex"); - Path feature = output.resolve(specificOutputName).resolve("classes.dex");; - validateUnobfuscatedOutput(base, feature); - } - - @Test - public void splitFilesObfuscation() - throws CompilationFailedException, IOException, ExecutionException { - // Initial normal compile to create dex files. - Path inputDex = temp.newFolder().toPath().resolve("input.zip"); - Path proguardMap = temp.getRoot().toPath().resolve("proguard.map"); - - R8.run( - R8Command.builder() - .setOutput(inputDex, OutputMode.DexIndexed) - .addProgramFiles(Paths.get(CLASS1_CLASS)) - .addProgramFiles(Paths.get(CLASS2_CLASS)) - .addProgramFiles(Paths.get(CLASS3_CLASS)) - .addProgramFiles(Paths.get(CLASS3_INNER_CLASS)) - .addProgramFiles(Paths.get(CLASS3_SYNTHETIC_CLASS)) - .addProgramFiles(Paths.get(CLASS4_CLASS)) - .addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE)) - .addLibraryFiles(ToolHelper.getDefaultAndroidJar()) - .setProguardMapOutputPath(proguardMap) - .addProguardConfiguration(getProguardConf(), Origin.unknown()) - .build()); - - Path outputDex = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(outputDex); - Path splitSpec = createSplitSpec(); - - DexSplitter.main( - new String[] { - "--input", inputDex.toString(), - "--output", outputDex.toString(), - "--feature-splits", splitSpec.toString(), - "--proguard-map", proguardMap.toString() - }); - - Path base = outputDex.resolve("base").resolve("classes.dex"); - Path feature = outputDex.resolve("feature1").resolve("classes.dex"); - String class3 = "dexsplitsample.Class3"; - // We should still be able to run the Class3 which we kept, it has a call to the obfuscated - // class1 which is in base. - ArtCommandBuilder builder = new ArtCommandBuilder(); - builder.appendClasspath(base.toString()); - builder.appendClasspath(feature.toString()); - builder.setMainClass(class3); - String out = ToolHelper.runArt(builder); - assertEquals(out, "Class3\n"); - - // Class1 should not be in the feature, it should still be in base. - builder = new ArtCommandBuilder(); - builder.appendClasspath(feature.toString()); - builder.setMainClass(class3); - try { - ToolHelper.runArt(builder); - assertFalse(true); - } catch (AssertionError assertionError) { - // We expect this to throw since base is not in the path and Class3 depends on Class1. - } - - // Ensure that the Class1 is actually in the correct split. Note that Class2 would have been - // shaken away. - CodeInspector inspector = new CodeInspector(base, proguardMap); - ClassSubject subject = inspector.clazz("dexsplitsample.Class1"); - assertTrue(subject.isPresent()); - assertTrue(subject.isRenamed()); - } - - @Test - public void splitNonClassFiles() - throws CompilationFailedException, IOException, FeatureMappingException { - Path inputZip = temp.getRoot().toPath().resolve("input-zip-with-non-class-files.jar"); - ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip)); - String name = "dexsplitsample/TextFile.txt"; - inputZipStream.putNextEntry(new ZipEntry(name)); - byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE)); - inputZipStream.write(fileBytes); - inputZipStream.closeEntry(); - name = "dexsplitsample/TextFile2.txt"; - inputZipStream.putNextEntry(new ZipEntry(name)); - inputZipStream.write(fileBytes); - inputZipStream.write(fileBytes); - inputZipStream.closeEntry(); - inputZipStream.close(); - Path output = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(output); - Path baseJar = temp.getRoot().toPath().resolve("base.jar"); - Path featureJar = temp.getRoot().toPath().resolve("feature1.jar"); - ZipOutputStream baseStream = new ZipOutputStream(Files.newOutputStream(baseJar)); - name = "dexsplitsample/TextFile.txt"; - baseStream.putNextEntry(new ZipEntry(name)); - baseStream.write(fileBytes); - baseStream.closeEntry(); - baseStream.close(); - ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar)); - name = "dexsplitsample/TextFile2.txt"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(fileBytes); - featureStream.write(fileBytes); - featureStream.closeEntry(); - featureStream.close(); - Options options = new Options(); - options.addInputArchive(inputZip.toString()); - options.setOutput(output.toString()); - options.addFeatureJar(baseJar.toString()); - options.addFeatureJar(featureJar.toString()); - options.setSplitNonClassResources(true); - DexSplitter.run(options); - Path baseDir = output.resolve("base"); - Path featureDir = output.resolve("feature1"); - byte[] contents = fileBytes; - byte[] contents2 = new byte[contents.length * 2]; - System.arraycopy(contents, 0, contents2, 0, contents.length); - System.arraycopy(contents, 0, contents2, contents.length, contents.length); - Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt"); - Path featureTextFile = featureDir.resolve("dexsplitsample/TextFile2.txt"); - assert Files.exists(baseTextFile); - assert Files.exists(featureTextFile); - assert Arrays.equals(Files.readAllBytes(baseTextFile), contents); - assert Arrays.equals(Files.readAllBytes(featureTextFile), contents2); - } - - @Test - public void splitDuplicateNonClassFiles() - throws IOException, CompilationFailedException, FeatureMappingException { - Path inputZip = temp.getRoot().toPath().resolve("input-zip-with-non-class-files.jar"); - ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip)); - String name = "dexsplitsample/TextFile.txt"; - inputZipStream.putNextEntry(new ZipEntry(name)); - byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE)); - inputZipStream.write(fileBytes); - inputZipStream.closeEntry(); - inputZipStream.close(); - Path output = temp.newFolder().toPath().resolve("output"); - Files.createDirectory(output); - Path featureJar = temp.getRoot().toPath().resolve("feature1.jar"); - Path feature2Jar = temp.getRoot().toPath().resolve("feature2.jar"); - ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar)); - name = "dexsplitsample/TextFile.txt"; - featureStream.putNextEntry(new ZipEntry(name)); - featureStream.write(fileBytes); - featureStream.closeEntry(); - featureStream.close(); - ZipOutputStream feature2Stream = new ZipOutputStream(Files.newOutputStream(feature2Jar)); - name = "dexsplitsample/TextFile.txt"; - feature2Stream.putNextEntry(new ZipEntry(name)); - feature2Stream.write(fileBytes); - feature2Stream.closeEntry(); - feature2Stream.close(); - Options options = new Options(); - options.addInputArchive(inputZip.toString()); - options.setOutput(output.toString()); - options.addFeatureJar(feature2Jar.toString()); - options.addFeatureJar(featureJar.toString()); - options.setSplitNonClassResources(true); - DexSplitter.run(options); - Path baseDir = output.resolve("base"); - Path feature1Dir = output.resolve("feature1"); - Path feature2Dir = output.resolve("feature2"); - Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt"); - Path feature1TextFile = feature1Dir.resolve("dexsplitsample/TextFile2.txt"); - Path feature2TextFile = feature2Dir.resolve("dexsplitsample/TextFile2.txt"); - assert !Files.exists(feature1TextFile); - assert !Files.exists(feature2TextFile); - assert Files.exists(baseTextFile); - assert Arrays.equals(Files.readAllBytes(baseTextFile), fileBytes); - } -}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java index 05d6d3d..bd7929d 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -17,12 +17,9 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ArtCommandBuilder; import com.android.tools.r8.ToolHelper.ProcessResult; -import com.android.tools.r8.dexsplitter.DexSplitter.Options; import com.android.tools.r8.utils.ArchiveResourceProvider; import com.android.tools.r8.utils.Pair; -import com.android.tools.r8.utils.ThrowingConsumer; import com.android.tools.r8.utils.ZipUtils; -import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; import dalvik.system.PathClassLoader; import java.io.IOException; @@ -31,9 +28,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.Set; -import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -155,96 +150,6 @@ toRun, baseOutput, r8TestCompileResult.getFeature(0), parameters.getRuntime()); } - // Compile the passed in classes plus RunInterface and SplitRunner using R8, then split - // based on the base/feature sets. toRun must implement the BaseRunInterface - <E extends Throwable> ProcessResult testDexSplitter( - TestParameters parameters, - Set<Class<?>> baseClasses, - Set<Class<?>> featureClasses, - Class<?> toRun, - String expectedOutput, - ThrowingConsumer<R8TestCompileResult, E> compileResultConsumer, - Consumer<R8FullTestBuilder> r8TestConfigurator) - throws Exception, E { - List<Class<?>> baseClassesWithRunner = - ImmutableList.<Class<?>>builder() - .add(RunInterface.class, SplitRunner.class) - .addAll(baseClasses) - .build(); - - Path baseJar = jarTestClasses(baseClassesWithRunner); - Path featureJar = jarTestClasses(featureClasses); - - Path featureOnly = - testForR8(parameters.getBackend()) - .addProgramClasses(featureClasses) - .addClasspathClasses(baseClasses) - .addClasspathClasses(RunInterface.class) - .addKeepAllClassesRule() - .addInliningAnnotations() - .setMinApi(parameters.getApiLevel()) - .compile() - .writeToZip(); - if (parameters.isDexRuntime()) { - // With D8 this should just work. We compile all of the base classes, then run with the - // feature loaded at runtime. Since there is no inlining/class merging we don't - // have any issues. - testForD8() - .addProgramClasses(SplitRunner.class, RunInterface.class) - .addProgramClasses(baseClasses) - .setMinApi(parameters.getApiLevel()) - .compile() - .run( - parameters.getRuntime(), - SplitRunner.class, - toRun.getName(), - featureOnly.toAbsolutePath().toString()) - .assertSuccessWithOutput(expectedOutput); - } - - R8FullTestBuilder builder = testForR8(parameters.getBackend()); - if (parameters.isCfRuntime()) { - // Compiling to jar we need to support the same way of loading code at runtime as - // android supports. - builder - .addProgramClasses(PathClassLoader.class) - .addKeepClassAndMembersRules(PathClassLoader.class); - } - - R8FullTestBuilder r8FullTestBuilder = - builder - .setMinApi(parameters.getApiLevel()) - .addProgramClasses(SplitRunner.class, RunInterface.class) - .addProgramClasses(baseClasses) - .addProgramClasses(featureClasses) - .addKeepMainRule(SplitRunner.class) - .addKeepClassRules(toRun); - r8TestConfigurator.accept(r8FullTestBuilder); - R8TestCompileResult r8TestCompileResult = r8FullTestBuilder.compile(); - compileResultConsumer.accept(r8TestCompileResult); - Path fullFiles = r8TestCompileResult.writeToZip(); - - // Ensure that we can run the program as a unit (i.e., without splitting) - r8TestCompileResult - .run(parameters.getRuntime(), SplitRunner.class, toRun.getName()) - .assertSuccessWithOutput(expectedOutput); - - Path splitterOutput = temp.newFolder().toPath(); - Path splitterBaseDexFile = splitterOutput.resolve("base").resolve("classes.dex"); - Path splitterFeatureDexFile = splitterOutput.resolve("feature").resolve("classes.dex"); - - Options options = new Options(); - options.setOutput(splitterOutput.toString()); - options.addBaseJar(baseJar.toString()); - options.addFeatureJar(featureJar.toString(), "feature"); - - options.addInputArchive(fullFiles.toString()); - DexSplitter.run(options); - - return runFeatureOnArt( - toRun, splitterBaseDexFile, splitterFeatureDexFile, parameters.getRuntime()); - } - ProcessResult runFeatureOnArt( Class toRun, Path splitterBaseDexFile, Path splitterFeatureDexFile, TestRuntime runtime) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java index 54f2fa4..6aaf325 100644 --- a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java +++ b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
@@ -75,8 +75,6 @@ } public void bar() { - // TODO(b/110175213): We cannot change this to an invoke-special since this requires a - // direct bridge in DEX. foo(); } }
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java index d3e91bb..ced7e4f 100644 --- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -97,7 +97,7 @@ ProgramMethod method = getMethod(inspector, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of()); assertFalse( appInfo - .resolveMethodOnClassHolder(method.getReference()) + .resolveMethodOnClassHolderLegacy(method.getReference()) .getSingleTarget() .isVirtualMethod()); assertNull(appInfo.lookupDirectTarget(method.getReference(), method)); @@ -181,14 +181,15 @@ assertFalse( appInfo - .resolveMethodOnClass(classTestSuper, methodXOnTestSuper.getReference()) + .resolveMethodOnClassLegacy(classTestSuper, methodXOnTestSuper.getReference()) .getSingleTarget() .isVirtualMethod()); assertNull( appInfo - .resolveMethodOnClass(classTest, methodXOnTestSuper.getReference()) + .resolveMethodOnClassLegacy(classTest, methodXOnTestSuper.getReference()) .getSingleTarget()); - assertNull(appInfo.resolveMethodOnClass(classTest, methodXOnTestReference).getSingleTarget()); + assertNull( + appInfo.resolveMethodOnClassLegacy(classTest, methodXOnTestReference).getSingleTarget()); assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper.getReference(), methodXOnTestSuper)); assertNull(appInfo.lookupDirectTarget(methodXOnTestReference, methodYOnTest));
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java index d05122a..d574d5a 100644 --- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,9 +40,14 @@ .addProgramClasses(I.class) .addProgramClassFileData(getClassWithTransformedInvoked()) .run(parameters.getRuntime(), Main.class) - .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class) // TODO(b/144410139): Consider making this a compilation failure when generating DEX. - .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!"); + .applyIf( + parameters.isCfRuntime(), + r -> r.assertFailureWithErrorThatThrows(VerifyError.class), + !(parameters.isDexRuntimeVersion(Version.V13_0_0) + && parameters.canUseDefaultAndStaticInterfaceMethods()), + r -> r.assertSuccessWithOutputLines("Hello World!"), + r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class)); } @Test @@ -54,14 +60,17 @@ .enableNoMethodStaticizingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class) // TODO(b/144410139): Consider making this a compilation failure when generating DEX. - .assertSuccessWithOutputLinesIf( + .applyIf( + parameters.isCfRuntime(), + r -> r.assertFailureWithErrorThatThrows(VerifyError.class), parameters.isDexRuntime() && !parameters.canUseDefaultAndStaticInterfaceMethods(), - "Hello World!") - .assertFailureWithErrorThatThrowsIf( - parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods(), - NullPointerException.class); + r -> r.assertSuccessWithOutputLines("Hello World!"), + parameters.isDexRuntime() + && !parameters.isDexRuntimeVersion(Version.V13_0_0) + && parameters.canUseDefaultAndStaticInterfaceMethods(), + r -> r.assertFailureWithErrorThatThrows(NullPointerException.class), + r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class)); } private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java index 956d59e..b8078ae 100644 --- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java +++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -72,11 +72,12 @@ // Check lookup will produce the same result. DexMethod id = method.getReference(); assertEquals( - appInfo().resolveMethodOnClass(id.holder, method.getReference()).getSingleTarget(), method); + appInfo().resolveMethodOnClassLegacy(id.holder, method.getReference()).getSingleTarget(), + method); // Check lookup targets with include method. MethodResolutionResult resolutionResult = - appInfo().resolveMethodOnClass(clazz, method.getReference()); + appInfo().resolveMethodOnClassLegacy(clazz, method.getReference()); AppInfoWithLiveness appInfo = null; // TODO(b/154881041): Remove or compute liveness. LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets( @@ -97,7 +98,7 @@ AppInfoWithLiveness appInfo = null; // TODO(b/154881041): Remove or compute liveness. LookupResultSuccess lookupResult = appInfo() - .resolveMethodOnInterface(clazz, method.getReference()) + .resolveMethodOnInterfaceLegacy(clazz, method.getReference()) .lookupVirtualDispatchTargets(clazz, appInfo(), appInfo, dexReference -> false) .asLookupResultSuccess(); assertNotNull(lookupResult);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java index a729899..68c055d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -99,7 +99,7 @@ } if (parameters.isDexRuntime()) { if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0) - || parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) { + || parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)) { return ImmutableList.of("true"); } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java index 6bbfb60..dc3ca19 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
@@ -33,7 +33,6 @@ .setMinApi(parameters.getApiLevel()) .addProgramClasses(Main.class) .addKeepMainRule(Main.class) - .addOptionsModification(options -> options.testing.enableExperimentalLoopUnrolling = true) .enableInliningAnnotations() .noMinification() .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java new file mode 100644 index 0000000..07efa85 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.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.loops; + +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 org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class LoopWith1IterationsEscape extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public LoopWith1IterationsEscape(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testLoopRemoved() throws Exception { + testForR8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .noMinification() + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("end 0", "iteration", "end 1"); + } + + public static class Main { + + public static void main(String[] args) { + loopExit(); + loopNoExit(); + } + + @NeverInline + public static void loopNoExit() { + Object[] objects = new Object[1]; + int i; + for (i = 0; i < objects.length; i++) { + if (System.currentTimeMillis() < 0) { + break; + } + System.out.println("iteration"); + } + System.out.println("end " + i); + } + + @NeverInline + public static void loopExit() { + Object[] objects = new Object[1]; + int i; + for (i = 0; i < objects.length; i++) { + if (System.currentTimeMillis() > 0) { + break; + } + System.out.println("iteration"); + } + System.out.println("end " + i); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java index 4c3f07d..29ab7c9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper.DexVm.Version; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,7 +37,11 @@ .setMinApi(parameters.getApiLevel()) .compile() .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines("1"); + .applyIf( + // See b/229706824. + parameters.getDexRuntimeVersion().equals(Version.V13_0_0), + r -> r.assertSuccessWithOutputLines("0"), + r -> r.assertSuccessWithOutputLines("1")); } static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java index 60a0b34..abf3664 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper.DexVm.Version; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,7 +37,11 @@ .setMinApi(parameters.getApiLevel()) .compile() .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutputLines("1"); + .applyIf( + // See b/229706824. + parameters.getDexRuntimeVersion().equals(Version.V13_0_0), + r -> r.assertSuccessWithOutputLines("0"), + r -> r.assertSuccessWithOutputLines("1")); } static class Main {
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java index cdc5b63..90660f4 100644 --- a/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java +++ b/src/test/java/com/android/tools/r8/jasmin/Regress65007724.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.D8TestRunResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm.Version; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -51,11 +50,6 @@ .setMinApi(parameters.getApiLevel()) .addProgramClassFileData(builder.buildClasses()) .run(parameters.getRuntime(), clazz.name); - if (parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) { - // See b/220821265 - d8TestRunResult.assertFailure(); - } else { - d8TestRunResult.assertSuccessWithOutput("Hello World!"); - } + d8TestRunResult.assertSuccessWithOutput("Hello World!"); } }
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java new file mode 100644 index 0000000..0602eb3 --- /dev/null +++ b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
@@ -0,0 +1,79 @@ +// 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.lightir; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.IntBox; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class LIRBasicCallbackTest extends TestBase { + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public LIRBasicCallbackTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + + @Test + public void test() throws Exception { + LIRCode code = LIRCode.builder().addConstNull().addConstInt(42).build(); + + // State to keep track of position in the byte array as we don't expose this in the iterator. + IntBox offset = new IntBox(0); + + LIRIterator it = code.iterator(); + + // The iterator and the elements are the same object providing a view on the byte stream. + assertTrue(it.hasNext()); + LIRInstructionView next = it.next(); + assertSame(it, next); + + it.accept( + (int opcode, int operandOffset, int operandSize) -> { + int headerSize = 1; + assertEquals(LIROpcodes.ACONST_NULL, opcode); + assertEquals(offset.get() + headerSize, operandOffset); + assertEquals(0, operandSize); + offset.increment(headerSize + operandSize); + }); + + assertTrue(it.hasNext()); + it.next(); + it.accept( + (int opcode, int operandOffset, int operandSize) -> { + int headerSize = 2; // opcode + payload-size + assertEquals(LIROpcodes.ICONST, opcode); + assertEquals(offset.get() + headerSize, operandOffset); + assertEquals(4, operandSize); + offset.increment(headerSize + operandSize); + }); + assertFalse(it.hasNext()); + + // The iterator can also be use in a normal java for-each loop. + // However, the item is not an actual item just a current view, so it can't be cached! + LIRInstructionView oldView = null; + for (LIRInstructionView view : code) { + if (oldView == null) { + oldView = view; + view.accept((opcode, ignore1, ignore2) -> assertEquals(LIROpcodes.ACONST_NULL, opcode)); + } else { + assertSame(oldView, view); + view.accept((opcode, ignore1, ignore2) -> assertEquals(LIROpcodes.ICONST, opcode)); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java index dd40ecf..75b7dda 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -25,8 +25,6 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.dex.ApplicationWriter; import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.dexsplitter.DexSplitter; -import com.android.tools.r8.dexsplitter.DexSplitter.Options; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.DexFileOverflowDiagnostic; import com.android.tools.r8.errors.Unreachable; @@ -255,49 +253,6 @@ } @Test - public void everyThirdClassInMainWithDexSplitter() throws Throwable { - List<String> featureMappings = new ArrayList<>(); - List<String> inFeatureMapping = new ArrayList<>(); - - ImmutableList.Builder<String> mainDexBuilder = ImmutableList.builder(); - for (int i = 0; i < MANY_CLASSES.size(); i++) { - String clazz = MANY_CLASSES.get(i); - // Write the first 2 classes into the split. - if (i < 10) { - featureMappings.add(clazz + ":feature1"); - inFeatureMapping.add(clazz); - } - if (i % 3 == 0) { - mainDexBuilder.add(clazz); - } - } - Path featureSplitMapping = temp.getRoot().toPath().resolve("splitmapping"); - Path mainDexFile = temp.getRoot().toPath().resolve("maindex"); - FileUtils.writeTextFile(featureSplitMapping, featureMappings); - List<String> mainDexList = mainDexBuilder.build(); - FileUtils.writeTextFile(mainDexFile, ListUtils.map(mainDexList, MainDexListTests::typeToEntry)); - Path output = temp.getRoot().toPath().resolve("split_output"); - Files.createDirectories(output); - TestDiagnosticsHandler diagnosticsHandler = new TestDiagnosticsHandler(); - Options options = new Options(diagnosticsHandler); - options.addInputArchive(getManyClassesMultiDexAppPath().toString()); - options.setFeatureSplitMapping(featureSplitMapping.toString()); - options.setOutput(output.toString()); - options.setMainDexList(mainDexFile.toString()); - DexSplitter.run(options); - assertEquals(0, diagnosticsHandler.numberOfErrorsAndWarnings()); - Path baseDir = output.resolve("base"); - CodeInspector inspector = - new CodeInspector( - AndroidApp.builder().addProgramFiles(baseDir.resolve("classes.dex")).build()); - for (String clazz : mainDexList) { - if (!inspector.clazz(clazz).isPresent() && !inFeatureMapping.contains(clazz)) { - failedToFindClassInExpectedFile(baseDir, clazz); - } - } - } - - @Test public void singleClassInMainDex() throws Throwable { ImmutableList<String> mainDex = ImmutableList.of(MANY_CLASSES.get(0)); verifyMainDexContains(mainDex, getManyClassesSingleDexAppPath(), true);
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java new file mode 100644 index 0000000..836c703 --- /dev/null +++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
@@ -0,0 +1,74 @@ +// 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.optimize.argumentpropagation; + +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +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 InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .enableNoHorizontalClassMergingAnnotations() + .enableNoVerticalClassMergingAnnotations() + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("B"); + } + + static class Main { + + public static void main(String[] args) { + I i = System.currentTimeMillis() > 0 ? new B() : new C(); + i.m(); + } + } + + @NoVerticalClassMerging + interface I { + + void m(); + } + + static class A {} + + @NoHorizontalClassMerging + static class B extends A implements I { + + @Override + public void m() { + System.out.println("B"); + } + } + + @NoHorizontalClassMerging + static class C extends A implements I { + + @Override + public void m() { + System.out.println("C"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java index 7ea76aa..5b061d7 100644 --- a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
@@ -50,18 +50,18 @@ }; DexEncodedMethod langObjectNotifyMethod = appInfo - .resolveMethodOnClassHolder( + .resolveMethodOnClassHolderLegacy( factory.createMethod(fooType, factory.createProto(factory.voidType), "notify")) .getSingleTarget(); for (DexType arrType : arrayTypes) { assertNull( appInfo - .resolveMethodOnClassHolder( + .resolveMethodOnClassHolderLegacy( factory.createMethod(arrType, factory.createProto(arrType), "clone")) .getSingleTarget()); DexEncodedMethod target = appInfo - .resolveMethodOnClassHolder( + .resolveMethodOnClassHolderLegacy( factory.createMethod(arrType, factory.createProto(factory.voidType), "notify")) .getSingleTarget(); assertEquals(langObjectNotifyMethod, target);
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokePolymorphicResolutionTest.java b/src/test/java/com/android/tools/r8/resolution/InvokePolymorphicResolutionTest.java index 1b0af62..db280ea 100644 --- a/src/test/java/com/android/tools/r8/resolution/InvokePolymorphicResolutionTest.java +++ b/src/test/java/com/android/tools/r8/resolution/InvokePolymorphicResolutionTest.java
@@ -48,7 +48,9 @@ MethodReference invokeExact = methodFromMethod(MethodHandle.class.getMethod("invokeExact", Object[].class)); MethodResolutionResult resolution1 = - appView.appInfo().resolveMethod(buildMethod(invokeExact, appView.dexItemFactory()), false); + appView + .appInfo() + .resolveMethodLegacy(buildMethod(invokeExact, appView.dexItemFactory()), false); assertFalse(resolution1.isFailedResolution()); // An inexact signature should also find invokeExact. @@ -61,7 +63,7 @@ MethodResolutionResult resolution2 = appView .appInfo() - .resolveMethod(buildMethod(inexactInvokeExact, appView.dexItemFactory()), false); + .resolveMethodLegacy(buildMethod(inexactInvokeExact, appView.dexItemFactory()), false); assertFalse(resolution2.isFailedResolution()); // The should both be the same MethodHandle.invokeExact method.
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java index 67007d2c..ebee166 100644 --- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java +++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -59,7 +59,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(Base.class, "collect", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); assertTrue(resolutionResult.isSingleResolution()); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java index 76cf55a..4c668d0 100644 --- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -194,7 +194,7 @@ buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory()); ProgramMethod context = appInfo.definitionForProgramType(reference.holder).getProgramDefaultInitializer(); - Assert.assertNotNull(appInfo.resolveMethodOnClassHolder(reference).getSingleTarget()); + Assert.assertNotNull(appInfo.resolveMethodOnClassHolderLegacy(reference).getSingleTarget()); DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(appView, reference, context, false); if (singleTargetHolderOrNull == null) { @@ -209,8 +209,8 @@ @Test public void lookupVirtualTargets() { DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory()); - Assert.assertNotNull(appInfo.resolveMethodOnClassHolder(method).getSingleTarget()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + Assert.assertNotNull(appInfo.resolveMethodOnClassHolderLegacy(method).getSingleTarget()); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); if (resolutionResult.isVirtualTarget()) { LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java index 9de13d2..4fbc7e7 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
@@ -91,7 +91,7 @@ @Test public void resolveTarget() { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB); + appInfo.resolveMethodOnClassLegacy(methodOnB.holder, methodOnB); DexClass context = appInfo.definitionFor(methodOnB.holder); assertTrue(resolutionResult.isIllegalAccessErrorResult(context, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java index d8ad8dd..77f847c 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
@@ -108,7 +108,7 @@ @Test public void testResolution() { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB); + appInfo.resolveMethodOnClassLegacy(methodOnB.holder, methodOnB); assertTrue(resolutionResult.isFailedResolution()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java index ca541ea..8118ef2 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -128,7 +128,7 @@ DexProgramClass bClass = appInfo.definitionForProgramType(methodOnBReference.holder); ProgramMethod methodOnB = bClass.lookupProgramMethod(methodOnBReference); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference); + appInfo.resolveMethodOnInterfaceLegacy(methodOnBReference.holder, methodOnBReference); DexEncodedMethod resolved = resolutionResult.getSingleTarget(); assertEquals(methodOnBReference, resolved.getReference()); assertFalse(resolutionResult.isVirtualTarget()); @@ -140,7 +140,7 @@ @Test public void lookupVirtualTargets() { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference); + appInfo.resolveMethodOnInterfaceLegacy(methodOnBReference.holder, methodOnBReference); DexEncodedMethod resolved = resolutionResult.getSingleTarget(); assertEquals(methodOnBReference, resolved.getReference()); assertFalse(resolutionResult.isVirtualTarget());
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java index c40f080..47c0857 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -175,7 +175,7 @@ public void lookupSingleTarget() { DexProgramClass bClass = appView.definitionForProgramType(methodOnB.holder); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB); + appInfo.resolveMethodOnClassLegacy(methodOnB.holder, methodOnB); DexEncodedMethod resolved = resolutionResult.getSingleTarget(); assertEquals(methodOnA, resolved.getReference()); assertFalse(resolutionResult.isVirtualTarget()); @@ -188,7 +188,7 @@ @Test public void lookupVirtualTargets() { MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnClass(methodOnB.holder, methodOnB); + appInfo.resolveMethodOnClassLegacy(methodOnB.holder, methodOnB); DexEncodedMethod resolved = resolutionResult.getSingleTarget(); assertEquals(methodOnA, resolved.getReference()); assertFalse(resolutionResult.isVirtualTarget());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java index 898cfae..aa827c8 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -122,7 +122,7 @@ // Resolve the method from the point of the declared holder. assertEquals(method.holder, declaredClassDefinition.type); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(declaredClassDefinition, method); + appInfo.resolveMethodOnLegacy(declaredClassDefinition, method); if (!symbolicReferenceIsDefiningType) { // The targeted method is a private interface method and thus not a maximally specific method.
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java index 82c6b63..50f4428 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -123,7 +123,7 @@ // Resolve the method from the point of the declared holder. assertEquals(method.holder, declaredClassDefinition.type); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(declaredClassDefinition, method); + appInfo.resolveMethodOnLegacy(declaredClassDefinition, method); // The targeted method is a private interface method and thus not a maximally specific method. assertTrue(resolutionResult instanceof NoSuchMethodResult);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java index b14ab55..1a6485c 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -96,7 +96,7 @@ // Resolve the method from the point of the declared holder. assertEquals(method.holder, declaredClassDefinition.type); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(declaredClassDefinition, method); + appInfo.resolveMethodOnLegacy(declaredClassDefinition, method); // Verify that the resolved method is on the defining class. assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java index 5638929..5fddce3 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -121,7 +121,7 @@ // Resolve the method from the point of the declared holder. assertEquals(method.holder, declaredClassDefinition.type); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(declaredClassDefinition, method); + appInfo.resolveMethodOnLegacy(declaredClassDefinition, method); // Resolution fails when there is a mismatch between the symbolic reference and the definition. if (!symbolicReferenceIsDefiningType) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java index d65dabf..31a8904 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -98,7 +98,7 @@ // Resolve the method from the point of the declared holder. assertEquals(method.holder, declaredClassDefinition.type); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOn(declaredClassDefinition, method); + appInfo.resolveMethodOnLegacy(declaredClassDefinition, method); // Verify that the resolved method is on the defining class. assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java index f1a35bb..63cd43e 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -74,7 +74,7 @@ DexProgramClass bClass = appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java index d68610e..f7bfe55 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -73,7 +73,7 @@ DexProgramClass bClass = appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java index ccc0430..7d9eb16 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -76,7 +76,7 @@ DexProgramClass bClass = appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java index f2f7dd9..4a9c475 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -72,7 +72,7 @@ DexProgramClass bClass = appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java index a91d3ed..754b19d 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
@@ -59,7 +59,7 @@ DexProgramClass aClass = appInfo.definitionFor(buildType(A.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java index 9ae877d..c5363de 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
@@ -65,7 +65,7 @@ DexProgramClass cClass = appInfo.definitionFor(buildType(C.class, appInfo.dexItemFactory())).asProgramClass(); DexMethod bar = buildMethod(B.class.getMethod("foo"), appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(bar); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(bar); assertEquals( OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificDifferentParentHierarchyTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificDifferentParentHierarchyTest.java new file mode 100644 index 0000000..856d3fe --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificDifferentParentHierarchyTest.java
@@ -0,0 +1,153 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRunResult; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f } + * J: J_L extends I { }, J_P { f } + * class Main implements I, J + * </pre> + */ +public class MaximallySpecificDifferentParentHierarchyTest extends TestBase { + + private static final String EXPECTED = "I::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(I.class), + ToolHelper.getClassFileForTestClass(J.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class)) + .addClassProgramData(getJProgram()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(Main.class) + .addProgramClassFileData(getJProgram()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + runTest(testForD8(parameters.getBackend())) + // TODO(b/214382176): Extend resolution to support multiple definition results. + .assertFailureWithErrorThatThrowsIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + IncompatibleClassChangeError.class) + .assertSuccessWithOutputLinesIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), EXPECTED); + } + + @Test + public void testR8() throws Exception { + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForR8(parameters.getBackend()).addKeepMainRule(Main.class)) + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); + } + + private TestRunResult<?> runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) + throws Exception { + return testBuilder + .addProgramClasses(Main.class) + .addProgramClassFileData(getJProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class); + } + + private byte[] getJProgram() throws Exception { + return transformer(JProgram.class).setClassDescriptor(descriptor(J.class)).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface JProgram { + default void foo() { + System.out.println("J_Program::foo"); + } + } + + public interface J extends I {} + + public static class Main implements I, J { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsICCETest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsICCETest.java new file mode 100644 index 0000000..2e57f9f --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsICCETest.java
@@ -0,0 +1,146 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f }, I_P { f } + * J: J_L { f }, J_P { f } + * class Main implements I,J + * </pre> + */ +public class MaximallySpecificMultiplePathsICCETest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(J.class), + ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles( + ToolHelper.getClassFileForTestClass(I.class), + ToolHelper.getClassFileForTestClass(J.class)) + .addClassProgramData(getMainWithInterfacesIAndJ()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(I.class, J.class) + .addProgramClassFileData(getMainWithInterfacesIAndJ()) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + runTest(testForD8(parameters.getBackend()), IncompatibleClassChangeError.class); + } + + @Test + public void testR8() throws Exception { + runTest( + testForR8(parameters.getBackend()).addKeepMainRule(Main.class), + IncompatibleClassChangeError.class); + } + + private void runTest( + TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder, Class<? extends Throwable> errorClass) + throws Exception { + testBuilder + .addProgramClasses(I.class, J.class) + .addProgramClassFileData(getMainWithInterfacesIAndJ()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(errorClass); + } + + private byte[] getMainWithInterfacesIAndJ() throws Exception { + return transformer(Main.class).setImplements(I.class, J.class).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface J { + default void foo() { + System.out.println("J::foo"); + } + } + + public static class Main implements I /*, J */ { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsSuccessTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsSuccessTest.java new file mode 100644 index 0000000..c743ac4 --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsSuccessTest.java
@@ -0,0 +1,167 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRunResult; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f }, I_P { } + * J: J_L { }, J_P { f } + * class Main implements I,J + * </pre> + */ +public class MaximallySpecificMultiplePathsSuccessTest extends TestBase { + + private static final String EXPECTED = "I::foo"; + // TODO(b/214382176): Extend resolution to support multiple definition results. + private static final String D8_R8_RESULT = "J::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(J.class), + ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class)) + .addClassProgramData(getIProgram()) + .addClassProgramData(getJProgram()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(Main.class) + .addProgramClassFileData(getIProgram(), getJProgram()) + .addDefaultRuntimeLibrary(parameters) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + boolean isDalvik = parameters.getDexRuntimeVersion().isDalvik(); + runTest(testForD8(parameters.getBackend())) + .assertSuccessWithOutputLinesIf(isDalvik, D8_R8_RESULT) + .assertSuccessWithOutputLinesIf( + !isDalvik && !parameters.canUseDefaultAndStaticInterfaceMethods(), D8_R8_RESULT) + // TODO(b/214382176): Extend resolution to support multiple definition results. + .assertSuccessWithOutputLinesIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), EXPECTED); + } + + @Test + public void testR8() throws Exception { + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForR8(parameters.getBackend()).addKeepMainRule(Main.class)) + .assertFailureWithErrorThatThrowsIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), NoSuchMethodError.class) + .assertSuccessWithOutputLinesIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), D8_R8_RESULT); + } + + private TestRunResult<?> runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) + throws Exception { + return testBuilder + .addProgramClasses(Main.class) + .addProgramClassFileData(getIProgram(), getJProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class); + } + + private byte[] getJProgram() throws Exception { + return transformer(JProgram.class).setClassDescriptor(descriptor(J.class)).transform(); + } + + private byte[] getIProgram() throws Exception { + return transformer(IProgram.class).setClassDescriptor(descriptor(I.class)).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface J {} + + public interface IProgram {} + + public interface JProgram { + default void foo() { + System.out.println("J::foo"); + } + } + + public static class Main implements I, J { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsThroughClassTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsThroughClassTest.java new file mode 100644 index 0000000..a14d33a --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificMultiplePathsThroughClassTest.java
@@ -0,0 +1,158 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f }, I_P { f } + * A: A_P implements I { } + * J: J_P { f } + * class Main extends A implements J + * </pre> + */ +public class MaximallySpecificMultiplePathsThroughClassTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles( + ToolHelper.getClassFileForTestClass(Main.class), + ToolHelper.getClassFileForTestClass(J.class)) + .addClassProgramData(getAWithImplementsI()) + .addClassProgramData(getIProgram()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(J.class, Main.class) + .addProgramClassFileData(getAWithImplementsI(), getIProgram()) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + runTest(testForD8(parameters.getBackend()), IncompatibleClassChangeError.class); + } + + @Test + public void testR8() throws Exception { + runTest( + testForR8(parameters.getBackend()).addKeepMainRule(Main.class), + IncompatibleClassChangeError.class); + } + + private void runTest( + TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder, Class<? extends Throwable> errorClass) + throws Exception { + testBuilder + .addProgramClasses(J.class, Main.class) + .addProgramClassFileData(getAWithImplementsI(), getIProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(errorClass); + } + + private byte[] getAWithImplementsI() throws Exception { + return transformer(A.class).setImplements(I.class).transform(); + } + + private byte[] getIProgram() throws Exception { + return transformer(IProgram.class).setClassDescriptor(descriptor(I.class)).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface IProgram { + default void foo() { + System.out.println("I_Program::foo"); + } + } + + public interface J { + default void foo() { + System.out.println("J::foo"); + } + } + + public static class A /* implements I */ {} + + public static class Main extends A implements J { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingAfterJoinTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingAfterJoinTest.java new file mode 100644 index 0000000..3e3b85b --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingAfterJoinTest.java
@@ -0,0 +1,156 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f } + * J: J_L extends I { f } + * K: K_L extends J { }, K_P extends J { } + * class Main implements I,K + * </pre> + */ +public class MaximallySpecificSingleDominatingAfterJoinTest extends TestBase { + + private static final String EXPECTED = "J::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(K.class), + ToolHelper.getClassFileForTestClass(J.class), + ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder.addProgramFiles( + ToolHelper.getClassFileForTestClass(K.class), + ToolHelper.getClassFileForTestClass(Main.class)); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(K.class, Main.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + runTest( + testForD8(parameters.getBackend()), + parameters.getDexRuntimeVersion().isDalvik() + ? VerifyError.class + // TODO(b/214382176): Extend resolution to support multiple definition results. + : AbstractMethodError.class); + } + + @Test + public void testR8() throws Exception { + runTest( + testForR8(parameters.getBackend()).addKeepMainRule(Main.class), AbstractMethodError.class); + } + + private void runTest( + TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder, + Class<? extends Throwable> errorIfNotSupportingDefaultMethods) + throws Exception { + testBuilder + .addProgramClasses(K.class, Main.class) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLinesIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), EXPECTED) + // TODO(b/214382176): Extend resolution to support multiple definition results. + .assertFailureWithErrorThatThrowsIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + errorIfNotSupportingDefaultMethods); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface J extends I { + @Override + default void foo() { + System.out.println("J::foo"); + } + } + + /* Present on both library and program */ + public interface K extends J {} + + public static class Main implements I, K { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingSubTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingSubTest.java new file mode 100644 index 0000000..630c284 --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleDominatingSubTest.java
@@ -0,0 +1,165 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f } + * J: J_L extends I { f }, J_P extends I { f } + * K: K_P extends J { f } + * class Main implements I,K + * </pre> + */ +public class MaximallySpecificSingleDominatingSubTest extends TestBase { + + private static final String EXPECTED = "K::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(J.class), + ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles( + ToolHelper.getClassFileForTestClass(K.class), + ToolHelper.getClassFileForTestClass(Main.class)) + .addClassProgramData(ImmutableList.of(getJOnProgram())); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(K.class, Main.class) + .addProgramClassFileData(getJOnProgram()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + runTest(testForD8(parameters.getBackend())); + } + + @Test + public void testR8() throws Exception { + runTest(testForR8(parameters.getBackend()).addKeepMainRule(Main.class)); + } + + private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception { + testBuilder + .addProgramClasses(K.class, Main.class) + .addProgramClassFileData(getJOnProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + private byte[] getJOnProgram() throws Exception { + return transformer(JProgram.class).setClassDescriptor(descriptor(J.class)).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + /* Present on both library and program */ + public interface JProgram extends I { + @Override + default void foo() { + System.out.println("J_Program::foo"); + ; + } + } + + public interface J extends I { + @Override + default void foo() { + System.out.println("J_Library::foo"); + ; + } + } + + public interface K extends J { + + @Override + default void foo() { + System.out.println("K::foo"); + } + } + + public static class Main implements I, K { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java new file mode 100644 index 0000000..c840cc2 --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleLibraryPartialTest.java
@@ -0,0 +1,146 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRunResult; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { f }, I_P { } + * class Main implements I + * </pre> + */ +public class MaximallySpecificSingleLibraryPartialTest extends TestBase { + + private static final String EXPECTED = "I::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder + .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class)) + .addClassProgramData(getIProgram()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClasses(Main.class) + .addProgramClassFileData(getIProgram()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForD8(parameters.getBackend())) + .assertFailureWithErrorThatThrowsIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + parameters.getDexRuntimeVersion().isDalvik() + ? VerifyError.class + : AbstractMethodError.class) + .assertSuccessWithOutputLinesIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), EXPECTED); + } + + @Test + public void testR8() throws Exception { + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForR8(parameters.getBackend()).addKeepMainRule(Main.class)) + .assertFailureWithErrorThatThrows(NoSuchMethodError.class); + } + + private TestRunResult<?> runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) + throws Exception { + return testBuilder + .addProgramClasses(Main.class) + .addProgramClassFileData(getIProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class); + } + + private byte[] getIProgram() throws Exception { + return transformer(IProgram.class).setClassDescriptor(descriptor(I.class)).transform(); + } + + public interface I { + default void foo() { + System.out.println("I::foo"); + } + } + + public interface IProgram {} + + public static class Main implements I { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java new file mode 100644 index 0000000..de9876c --- /dev/null +++ b/src/test/java/com/android/tools/r8/resolution/duplicatedefinitions/MaximallySpecificSingleProgramPartialTest.java
@@ -0,0 +1,146 @@ +// 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.resolution.duplicatedefinitions; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRunResult; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import java.nio.file.Path; +import org.junit.Before; +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) +/** + * This is testing resolving Main.f for: + * + * <pre> + * I: I_L { }, I_P { f } + * class Main implements I + * </pre> + */ +public class MaximallySpecificSingleProgramPartialTest extends TestBase { + + private static final String UNEXPECTED = "I::foo"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private Path libraryClasses; + + @Before + public void setup() throws Exception { + libraryClasses = temp.newFile("lib.jar").toPath(); + ZipBuilder.builder(libraryClasses) + .addFilesRelative( + ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(I.class)) + .build(); + } + + @Test + public void testResolution() throws Exception { + assumeTrue(parameters.useRuntimeAsNoneRuntime()); + AndroidApp.Builder builder = AndroidApp.builder(); + builder.addClassProgramData(getMainImplementingI()).addClassProgramData(getIProgram()); + builder.addLibraryFiles(parameters.getDefaultRuntimeLibrary(), libraryClasses); + AppView<AppInfoWithClassHierarchy> appView = + computeAppViewWithClassHierarchy( + builder.build(), null, options -> options.loadAllClassDefinitions = true); + AppInfoWithClassHierarchy appInfo = appView.appInfo(); + DexMethod method = buildNullaryVoidMethod(Main.class, "foo", appInfo.dexItemFactory()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + assertThrows( + Unreachable.class, + () -> { + appInfo.unsafeResolveMethodDueToDexFormat(method); + }); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addRunClasspathFiles(libraryClasses) + .addProgramClassFileData(getMainImplementingI(), getIProgram()) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(NoSuchMethodError.class); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForD8(parameters.getBackend())) + .assertFailureWithErrorThatThrowsIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), NoSuchMethodError.class) + .assertSuccessWithOutputLinesIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), UNEXPECTED); + } + + @Test + public void testR8() throws Exception { + // TODO(b/214382176): Extend resolution to support multiple definition results. + runTest(testForR8(parameters.getBackend()).addKeepMainRule(Main.class)) + .assertFailureWithErrorThatThrowsIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), NoSuchMethodError.class) + .assertSuccessWithOutputLinesIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), UNEXPECTED); + } + + private TestRunResult<?> runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) + throws Exception { + return testBuilder + .addProgramClassFileData(getMainImplementingI(), getIProgram()) + .addDefaultRuntimeLibrary(parameters) + .addLibraryFiles(libraryClasses) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(options -> options.loadAllClassDefinitions = true) + .compile() + .addBootClasspathFiles(buildOnDexRuntime(parameters, libraryClasses)) + .run(parameters.getRuntime(), Main.class); + } + + private byte[] getIProgram() throws Exception { + return transformer(IProgram.class).setClassDescriptor(descriptor(I.class)).transform(); + } + + private byte[] getMainImplementingI() throws Exception { + return transformer(Main.class).setImplements(I.class).transform(); + } + + public interface I {} + + public interface IProgram { + default void foo() { + System.out.println("I::foo"); + } + } + + public static class Main implements /* I */ IProgram { + + public static void main(String[] args) { + new Main().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java index 81a0f45..51f29aa 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -45,7 +45,7 @@ buildClasses(CLASSES).addLibraryFile(parameters.getDefaultRuntimeLibrary()).build(); AppInfoWithLiveness appInfo = computeAppViewWithLiveness(app, Main.class).appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); // Currently R8 will resolve to L::f as that is the first in the topological search. // Resolution may return any of the matches, so it is valid if this expectation changes.
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java index d708d1e..05bb852 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -51,7 +51,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java index 8f94ef3..7314ee5 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -51,7 +51,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java index 6b31505..9f1df3e 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -53,7 +53,7 @@ Main.class) .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java index 0b78a07..b5cac2e 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -53,7 +53,7 @@ Main.class) .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java index 7f09e71..31e9e9e 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -53,7 +53,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); Set<String> holders = new HashSet<>(); resolutionResult .asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java index 0f37198..53a17c4 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -50,7 +50,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java index 8d35771..018cddb 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -50,7 +50,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget(); assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString()); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java index 290dbec..d5aefd7 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -58,7 +58,7 @@ .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); if (minApi.isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) { // When desugaring a forwarding method throwing ICCE is inserted. // Check that the resolved method throws such an exception.
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java index b3c88c7..e838d5d 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -64,7 +64,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java index 1b3d3d9..dfa7451 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -63,7 +63,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java index 8908dce..ed99f3e 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -63,7 +63,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo); @@ -112,7 +112,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java index 8122941..9002d8d 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -63,7 +63,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java index ca5e24e..29ae548 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -56,7 +56,7 @@ AssertionError.class, () -> appInfo - .resolveMethodOnInterfaceHolder(method) + .resolveMethodOnInterfaceHolderLegacy(method) .lookupVirtualDispatchTargets(context, appInfo)); }
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java index 0819b97..6d93202 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -63,7 +63,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(J.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java index 5912e69..8b1b588 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
@@ -61,7 +61,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java index 3503bdf..c48764d 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
@@ -61,7 +61,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java index 4d85bfb..50e000b 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
@@ -63,7 +63,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java index 682d447..a3b1d8f 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
@@ -60,7 +60,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java index bbf73d7..1d8a03a 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
@@ -60,7 +60,7 @@ AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = - appInfo.resolveMethodOnInterface(method.holder, method); + appInfo.resolveMethodOnInterfaceLegacy(method.holder, method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java index bff70ca..13a108d 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -74,7 +74,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(Abstract.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Abstract.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java index 255fbed..45eab24 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
@@ -59,7 +59,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java index f33ee0b..a875b43 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
@@ -63,7 +63,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo); @@ -86,7 +86,8 @@ .addProgramClassFileData(getDWithPackagePrivateFoo()) .run(parameters.getRuntime(), Main.class); if (parameters.isCfRuntime() - || parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET)) { + || parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_TARGET) + || parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_13_0_0_TARGET)) { runResult.assertSuccessWithOutputLines(EXPECTED); } else { runResult.assertSuccessWithOutputLines(EXPECTED_ART);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java index be8acd3..3eb13d5 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
@@ -73,7 +73,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java index 8846bf2..6ccf173 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -67,7 +67,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); assertTrue(resolutionResult.isAccessibleFrom(context, appView).isFalse());
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java index da660f2..7a588f3 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
@@ -60,7 +60,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java index 58c64b1..42733a1 100644 --- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java +++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -131,7 +131,7 @@ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory()); DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolution = appInfo.resolveMethodOnClassHolder(fooA); + MethodResolutionResult resolution = appInfo.resolveMethodOnClassHolderLegacy(fooA); DexProgramClass context = appView.definitionForProgramType(typeMain); DexProgramClass upperBound = appView.definitionForProgramType(typeA); DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java index e856ed2..2906ddd 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -59,7 +59,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java index 665ef74..42c441b 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -62,7 +62,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java index df0722f..67afdad 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -62,7 +62,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java index d4541ac..c6c6aac 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -63,7 +63,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java index 6aac0fb..ada09f0 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
@@ -59,7 +59,8 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = + appInfo.resolveMethodOnClassHolderLegacy(method); assertTrue(resolutionResult.isSingleResolution()); DexType mainType = buildType(Main.class, appInfo.dexItemFactory()); DexProgramClass main = appView.definitionForProgramType(mainType);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java index a1f6b17..184b03f 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -62,7 +62,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java index 19f951a..a96b487 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -87,7 +87,7 @@ }); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(initial, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = DexProgramClass.asProgramClassOrNull( appView @@ -238,7 +238,7 @@ .build()); AppInfoWithClassHierarchy appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexType typeA = buildType(A.class, appInfo.dexItemFactory()); DexType typeB = buildType(B.class, appInfo.dexItemFactory()); DexProgramClass classB = appInfo.definitionForProgramType(typeB); @@ -273,7 +273,7 @@ Unrelated.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(Unrelated.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java index 33bfc01..69ef032 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -62,7 +62,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(Top.class, "clear", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(TopRunner.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java index 28d1cc2..b72f8b6 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -68,7 +68,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType( buildType(ViewModelRunner.class, appInfo.dexItemFactory())); @@ -119,7 +119,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo); @@ -171,7 +171,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType( buildType(ViewModelRunnerWithCast.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java index 24aa358..2d10b18 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
@@ -61,7 +61,7 @@ Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(B.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java index 3ffdf09..87d27ee 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
@@ -54,7 +54,7 @@ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(builder.build(), Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java index 337ba95..0a3d14a 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
@@ -50,7 +50,7 @@ PackagePrivateChainTest.Main.class); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClassHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java index 8769eca..d1eb536 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -63,7 +63,7 @@ .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolder(method); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterfaceHolderLegacy(method); DexProgramClass context = appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory())); LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java index d68447d..e7b884c 100644 --- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java +++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -34,12 +34,6 @@ private static final String JAVAC_LAMBDA_METHOD = "lambda$main$0"; - // TODO(b/172014416): These should not be needed once fixed. - private static final String LAMBDA_BRIDGE_METHOD = "$r8$lambda$dX5OYTAgq4ijGUv_zaGoVsFINMs"; - private static final String INTERNAL_LAMBDA_CLASS = - Main.class.getTypeName() - + "$$InternalSyntheticLambda$0$11a5d582ed94e937718cf3ed497d4d164b60dfa85d606466457007fade57dce8$0"; - @Parameters(name = "{0}") public static TestParametersCollection parameters() { return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java index eb1d9f3..f5943b5 100644 --- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java +++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineSourceFileContextStackTrace.java
@@ -40,11 +40,9 @@ public List<String> retracedStackTrace() { return Arrays.asList( " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary" - // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt - + ".throwsException(KotlinJavaSourceFileTestLibrary.java:22)", + + ".throwsException(KotlinJavaSourceFileTestLibrary.kt:22)", " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary" - // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt - + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.java:19)", + + ".callsThrowsException(KotlinJavaSourceFileTestLibrary.kt:19)", " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject" + ".main(KotlinJavaSourceFileTestObject.java:32)"); } @@ -53,11 +51,9 @@ public List<String> retraceVerboseStackTrace() { return Arrays.asList( " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary" - // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt - + ".void throwsException()(KotlinJavaSourceFileTestLibrary.java:22)", + + ".void throwsException()(KotlinJavaSourceFileTestLibrary.kt:22)", " at com.google.appreduce.remapper.KotlinJavaSourceFileTestLibrary" - // TODO(b/226885646): Should be KotlinJavaSourceFileTestLibrary.kt - + ".void callsThrowsException()(KotlinJavaSourceFileTestLibrary.java:19)", + + ".void callsThrowsException()(KotlinJavaSourceFileTestLibrary.kt:19)", " at com.google.appreduce.remapper.KotlinJavaSourceFileTestObject" + ".void main(java.lang.String[])(KotlinJavaSourceFileTestObject.java:32)"); }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java index 84bd1ef..b70b311 100644 --- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.rewrite.assertions; -import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.R8FullTestBuilder; +import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.TestBuilder; import com.android.tools.r8.TestCompilerBuilder; import com.android.tools.r8.TestParameters; import com.android.tools.r8.references.MethodReference; @@ -70,10 +71,21 @@ protected abstract List<Path> getKotlinFiles() throws IOException; + protected boolean transformKotlinClasses() { + return false; + } + + protected byte[] transformedKotlinClasses(Path kotlinClasses) throws IOException { + assert false; + return null; + } + protected abstract String getTestClassName(); protected void configureR8(R8FullTestBuilder builder) {} + protected void configureResultR8(R8TestCompileResult builder) {} + private Path kotlinStdlibLibraryForRuntime() throws Exception { Path kotlinStdlibCf = kotlinc.getKotlinStdlibJar(); if (parameters.getRuntime().isCf()) { @@ -107,6 +119,16 @@ } } + private void addKotlinClasses(TestBuilder<?, ?> builder) { + builder.applyIf( + transformKotlinClasses(), + b -> + b.addProgramClassFileData( + transformedKotlinClasses( + compiledForAssertions.getForConfiguration(kotlinc, targetVersion))), + b -> b.addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))); + } + @Test public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); @@ -114,7 +136,7 @@ .apply(this::configureKotlinStdlib) .setMinApi(parameters.getApiLevel()) .addProgramClasses(AssertionHandlers.class) - .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) + .apply(this::addKotlinClasses) .addAssertionsConfiguration( builder -> builder @@ -131,7 +153,7 @@ .apply(this::configureKotlinStdlib) .setMinApi(parameters.getApiLevel()) .addProgramClasses(AssertionHandlers.class) - .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) + .apply(this::addKotlinClasses) .addAssertionsConfiguration( builder -> builder @@ -140,13 +162,8 @@ .build()) .addKeepMainRule(getTestClassName()) .apply(this::configureR8) - .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary) .compile() - .applyIf( - !kotlinStdlibAsLibrary, - result -> - result.assertAllWarningMessagesMatch( - equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))) + .apply(this::configureResultR8) .run(parameters.getRuntime(), getTestClassName()) .assertSuccessWithOutput(getExpectedOutput()); }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithConditionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithConditionsTest.java new file mode 100644 index 0000000..af2c8db --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithConditionsTest.java
@@ -0,0 +1,41 @@ +// 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.rewrite.assertions; + +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers; +import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsWithConditions; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AssertionConfigurationAssertionHandlerWithConditionsTest + extends AssertionConfigurationAssertionHandlerTestBase { + + private static final String EXPECTED_OUTPUT = + StringUtils.lines( + "assertionHandler: assertionWithSimpleCondition", + "assertionHandler: assertionWithCondition"); + + @Override + String getExpectedOutput() { + return EXPECTED_OUTPUT; + } + + @Override + MethodReference getAssertionHandler() throws Exception { + return Reference.methodFromMethod( + AssertionHandlers.class.getMethod("assertionHandler", Throwable.class)); + } + + @Override + List<Class<?>> getTestClasses() { + return ImmutableList.of(AssertionsWithConditions.class); + } +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithConditions.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithConditions.java new file mode 100644 index 0000000..17d4d54 --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithConditions.java
@@ -0,0 +1,33 @@ +// 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.rewrite.assertions.assertionhandler; + +import com.android.tools.r8.Keep; + +public class AssertionsWithConditions { + + @Keep + public static void assertionWithSimpleCondition(int x) { + assert x < 0; + } + + private static boolean isZero(int x) { + return x == 0; + } + + private static boolean isNegative(int x) { + return x < 0; + } + + @Keep + public static void assertionWithCondition(int x, int y, int z) { + assert x == 0 && isZero(y) && isNegative(z); + } + + public static void main(String[] args) { + assertionWithSimpleCondition(args.length); + assertionWithCondition(args.length, args.length, args.length); + } +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java new file mode 100644 index 0000000..d0177bb --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest.java
@@ -0,0 +1,97 @@ +// 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.rewrite.assertions.kotlinassertionhandlerdoublecheck; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion; +import com.android.tools.r8.KotlinTestParameters; +import com.android.tools.r8.R8FullTestBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase; +import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.FileUtils; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.ZipUtils; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest + extends AssertionConfigurationAssertionHandlerKotlinTestBase { + + public AssertionConfigurationAssertionHandlerKotlinDoubleCheckTest( + TestParameters parameters, + KotlinTestParameters kotlinParameters, + boolean kotlinStdlibAsClasspath, + boolean useJvmAssertions) + throws IOException { + super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions); + } + + @Override + protected String getExpectedOutput() { + return StringUtils.lines("assertionHandler: doubleCheckAssertion"); + } + + @Override + protected MethodReference getAssertionHandler() throws Exception { + return Reference.methodFromMethod( + AssertionHandlers.class.getMethod("assertionHandler", Throwable.class)); + } + + @Override + protected List<Path> getKotlinFiles() throws IOException { + return getKotlinFilesInTestPackage(getClass().getPackage()); + } + + @Override + protected boolean transformKotlinClasses() { + return true; + } + + @Override + protected byte[] transformedKotlinClasses(Path kotlinClasses) throws IOException { + Path compiledKotlinClasses = temp.newFolder().toPath(); + ZipUtils.unzip( + compiledForAssertions.getForConfiguration(kotlinc, targetVersion), compiledKotlinClasses); + String testClassPath = + DescriptorUtils.getPackageBinaryNameFromJavaType(getTestClassName()) + + FileUtils.CLASS_EXTENSION; + String assertionsMockBinaryName = + DescriptorUtils.getPackageBinaryNameFromJavaType( + getClass().getPackage().getName() + ".AssertionsMock"); + // Rewrite the static get on AssertionsMock.Enabled to static get on kotlin._Assertions.ENABLED + return transformer( + compiledKotlinClasses.resolve(testClassPath), + Reference.classFromTypeName(getTestClassName())) + .transformFieldInsnInMethod( + "doubleCheckAssertionsEnabled", + (opcode, owner, name, descriptor, continuation) -> { + continuation.visitFieldInsn( + opcode, + owner.equals(assertionsMockBinaryName) ? "kotlin/_Assertions" : owner, + name, + descriptor); + }) + .transform(); + } + + @Override + protected String getTestClassName() { + return getClass().getPackage().getName() + ".AssertionDoubleCheckKt"; + } + + @Override + protected void configureR8(R8FullTestBuilder builder) { + boolean referencesNotNull = + !kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72) && !kotlinStdlibAsLibrary; + builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull")); + } +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt new file mode 100644 index 0000000..3351bcd --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionDoubleCheck.kt
@@ -0,0 +1,28 @@ +// 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.rewrite.assertions.kotlinassertionhandlerdoublecheck + +fun doubleCheckAssertionsEnabled() { + // AssertionsMock.ENABLED will be rewritten to kotlin._Assertions.ENABLED to + // test + // * two checks on kotlin._Assertions.ENABLED and + // * check on both kotlin._Assertions.ENABLED and $assertionsDisabled + // before entering the assertion code. + // + // This is testing code like this found in kotlin-stdlib: + // + // if (_Assertions.ENABLED) + // assert(...) { ... } + // } + // + // E.g. in kotlin/io/files/FileTreeWalk.kt. + if (AssertionsMock.ENABLED) { + assert(false) { "doubleCheckAssertion" } + } +} + +fun main() { + doubleCheckAssertionsEnabled(); +} \ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt new file mode 100644 index 0000000..bd05154 --- /dev/null +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerdoublecheck/AssertionsMock.kt
@@ -0,0 +1,10 @@ +// 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.rewrite.assertions.kotlinassertionhandlerdoublecheck + +object AssertionsMock { + @JvmField + val ENABLED: Boolean = false +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java index e460253..aad753e 100644 --- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -4,9 +4,12 @@ package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlersimple; +import static org.hamcrest.CoreMatchers.equalTo; + import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.R8FullTestBuilder; +import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.Reference; @@ -62,6 +65,17 @@ !kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72) && !kotlinStdlibAsLibrary && !useJvmAssertions; - builder.applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull")); + builder + .applyIf(referencesNotNull, b -> b.addDontWarn("org.jetbrains.annotations.NotNull")) + .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary); + } + + @Override + protected void configureResultR8(R8TestCompileResult builder) { + builder.applyIf( + !kotlinStdlibAsLibrary, + result -> + result.assertAllWarningMessagesMatch( + equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))); } }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java index 93b3b57..c2848c3 100644 --- a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
@@ -4,8 +4,11 @@ package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlerwithexceptions; +import static org.hamcrest.CoreMatchers.equalTo; + import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.R8FullTestBuilder; +import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.references.Reference; @@ -66,6 +69,16 @@ builder .addKeepRules("-keep class **.*Kt { assertionsWith*(); simpleAssertion(); }") .addDontWarn("org.jetbrains.annotations.NotNull") - .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable")); + .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable")) + .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary); + } + + @Override + protected void configureResultR8(R8TestCompileResult builder) { + builder.applyIf( + !kotlinStdlibAsLibrary, + result -> + result.assertAllWarningMessagesMatch( + equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))); } }
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java index 604caae..bb63e6f 100644 --- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java +++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -86,7 +86,7 @@ "Hello!", "Unexpected outcome of checkcast", "Goodbye!", ""); case V7_0_0: - case V13_MASTER: + case V13_0_0: return StringUtils.joinLines( "Hello!", "Unexpected outcome of checkcast",
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java index 3a32244..d3b8c48 100644 --- a/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java +++ b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java
@@ -100,7 +100,7 @@ DexMethod helloReference = buildNullaryVoidMethod(HelloGreeter.class, "hello", dexItemFactory); assertTrue( appInfo - .resolveMethodOnClassHolder(helloReference) + .resolveMethodOnClassHolderLegacy(helloReference) .isAccessibleFrom(context, appView) .isTrue()); @@ -108,7 +108,7 @@ DexMethod worldReference = buildNullaryVoidMethod(WorldGreeter.class, "world", dexItemFactory); assertTrue( appInfo - .resolveMethodOnClassHolder(worldReference) + .resolveMethodOnClassHolderLegacy(worldReference) .isAccessibleFrom(context, appView) .isFalse()); }
diff --git a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.java b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.java new file mode 100644 index 0000000..c61fa16 --- /dev/null +++ b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.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.shaking.methods.interfaces; + +import static com.android.tools.r8.references.Reference.classFromClass; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class MultipleRulesRegression228791247Test extends TestBase { + + private static final String EXPECTED = StringUtils.lines("Hello!"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MultipleRulesRegression228791247Test(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(I.class, J.class, A.class, TestClass.class) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testR8() throws Exception { + // Regression adds two rules causes the forwarding method to be generated twice. + String rule1 = "-keep class " + classFromClass(J.class).getTypeName() + "{ void *oo(); }"; + String rule2 = "-keep class " + classFromClass(J.class).getTypeName() + "{ void fo*(); }"; + testForR8(parameters.getBackend()) + .addProgramClasses(I.class, J.class, A.class, TestClass.class) + .addKeepMainRule(TestClass.class) + .addKeepRules(rule1, rule2) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + public interface I { + default void foo() { + if (System.nanoTime() > 0) { + System.out.println("Hello!"); + } + } + } + + public interface J extends I { + // No foo, but it will be kept at J::foo. + } + + public static class A implements J {} + + public static class TestClass { + + public static void main(String[] args) { + J j = System.nanoTime() > 0 ? new A() : null; + j.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java b/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java deleted file mode 100644 index f41be5c..0000000 --- a/src/test/java/com/android/tools/r8/utils/FeatureClassMappingTest.java +++ /dev/null
@@ -1,132 +0,0 @@ -// Copyright (c) 2016, 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.utils; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertFalse; - -import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException; -import com.google.common.collect.ImmutableList; -import java.util.List; -import org.junit.Test; - -public class FeatureClassMappingTest { - - @Test - public void testSimpleParse() throws Exception { - - List<String> lines = - ImmutableList.of( - "# Comment, don't care about contents: even more ::::", - "com.google.base:base", - "", // Empty lines allowed - "com.google.feature1:feature1", - "com.google.feature1:feature1", // Multiple definitions of the same predicate allowed. - "com.google$:feature1", - "_com.google:feature21", - "com.google.*:feature32"); - FeatureClassMapping mapping = new FeatureClassMapping(lines); - } - - private void ensureThrowsMappingException(List<String> lines) { - try { - new FeatureClassMapping(lines); - assertFalse(true); - } catch (FeatureMappingException e) { - // Expected - } - } - - private void ensureThrowsMappingException(String string) { - ensureThrowsMappingException(ImmutableList.of(string)); - } - - @Test - public void testLookup() throws Exception { - List<String> lines = - ImmutableList.of( - "com.google.Base:base", - "", - "com.google.Feature1:feature1", - "com.google.Feature1:feature1", // Multiple definitions of the same predicate allowed. - "com.google.different.*:feature1", - "_com.Google:feature21", - "com.google.bas42.*:feature42"); - FeatureClassMapping mapping = new FeatureClassMapping(lines); - assertEquals(mapping.featureForClass("com.google.Feature1"), "feature1"); - assertEquals(mapping.featureForClass("com.google.different.Feature1"), "feature1"); - assertEquals(mapping.featureForClass("com.google.different.Foobar"), "feature1"); - assertEquals(mapping.featureForClass("com.google.Base"), "base"); - assertEquals(mapping.featureForClass("com.google.bas42.foo.bar.bar.Foo"), "feature42"); - assertEquals(mapping.featureForClass("com.google.bas42.f$o$o$.bar43.bar.Foo"), "feature42"); - assertEquals(mapping.featureForClass("_com.Google"), "feature21"); - } - - @Test - public void testCatchAllWildcards() throws Exception { - testBaseWildcard(true); - testBaseWildcard(false); - testNonBaseCatchAll(); - } - - private void testNonBaseCatchAll() throws FeatureMappingException { - List<String> lines = - ImmutableList.of( - "com.google.Feature1:feature1", - "*:nonbase", - "com.strange.*:feature2"); - FeatureClassMapping mapping = new FeatureClassMapping(lines); - assertEquals(mapping.featureForClass("com.google.Feature1"), "feature1"); - assertEquals(mapping.featureForClass("com.google.different.Feature1"), "nonbase"); - assertEquals(mapping.featureForClass("com.strange.different.Feature1"), "feature2"); - assertEquals(mapping.featureForClass("Feature1"), "nonbase"); - assertEquals(mapping.featureForClass("a.b.z.A"), "nonbase"); - } - - private void testBaseWildcard(boolean explicitBase) throws FeatureMappingException { - List<String> lines = - ImmutableList.of( - "com.google.Feature1:feature1", - explicitBase ? "*:base" : "", - "com.strange.*:feature2"); - FeatureClassMapping mapping = new FeatureClassMapping(lines); - assertEquals(mapping.featureForClass("com.google.Feature1"), "feature1"); - assertEquals(mapping.featureForClass("com.google.different.Feature1"), "base"); - assertEquals(mapping.featureForClass("com.strange.different.Feature1"), "feature2"); - assertEquals(mapping.featureForClass("com.stranger.Clazz"), "base"); - assertEquals(mapping.featureForClass("Feature1"), "base"); - assertEquals(mapping.featureForClass("a.b.z.A"), "base"); - } - - @Test - public void testWrongLines() throws Exception { - // No colon. - ensureThrowsMappingException("foo"); - ensureThrowsMappingException("com.google.base"); - // Two colons. - ensureThrowsMappingException(ImmutableList.of("a:b:c")); - - // Empty identifier. - ensureThrowsMappingException("com..google:feature1"); - - // Ambiguous redefinition - ensureThrowsMappingException( - ImmutableList.of("com.google.foo:feature1", "com.google.foo:feature2")); - ensureThrowsMappingException( - ImmutableList.of("com.google.foo.*:feature1", "com.google.foo.*:feature2")); - } - - @Test - public void testUsesOnlyExactMappings() throws Exception { - List<String> lines = - ImmutableList.of( - "com.pkg1.Clazz:feature1", - "com.pkg2.Clazz:feature2"); - FeatureClassMapping mapping = new FeatureClassMapping(lines); - - assertEquals(mapping.featureForClass("com.pkg1.Clazz"), "feature1"); - assertEquals(mapping.featureForClass("com.pkg2.Clazz"), "feature2"); - assertEquals(mapping.featureForClass("com.pkg1.Other"), mapping.baseName); - } -}
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 index 27356f7..0f2a679 100644 --- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 +++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@ -175091160988fe534b90b70308ac0f7c489e48ec \ No newline at end of file +cec0d3cf2655ac9c77410ddf87a7b4e5900858a5 \ No newline at end of file
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py index b363bb7..a4a010b 100755 --- a/tools/apk_masseur.py +++ b/tools/apk_masseur.py
@@ -45,9 +45,6 @@ apk = args[0] return (options, apk) -def findKeystore(): - return os.path.join(os.getenv('HOME'), '.android', 'app.keystore') - def repack(apk, processed_out, resources, temp, quiet, logging): processed_apk = os.path.join(temp, 'processed.apk') shutil.copyfile(apk, processed_apk) @@ -80,25 +77,13 @@ def sign(unsigned_apk, keystore, temp, quiet, logging): signed_apk = os.path.join(temp, 'unaligned.apk') - apk_utils.sign_with_apksigner( + return apk_utils.sign_with_apksigner( unsigned_apk, signed_apk, keystore, quiet=quiet, logging=logging) - return signed_apk def align(signed_apk, temp, quiet, logging): utils.Print('Aligning', quiet=quiet) aligned_apk = os.path.join(temp, 'aligned.apk') - zipalign_path = ( - 'zipalign' if 'build_tools' in os.environ.get('PATH') - else os.path.join(utils.getAndroidBuildTools(), 'zipalign')) - cmd = [ - zipalign_path, - '-f', - '4', - signed_apk, - aligned_apk - ] - utils.RunCmd(cmd, quiet=quiet, logging=logging) - return signed_apk + return apk_utils.align(signed_apk, aligned_apk) def masseur( apk, dex=None, resources=None, out=None, adb_options=None, keystore=None, @@ -106,7 +91,7 @@ if not out: out = os.path.basename(apk) if not keystore: - keystore = findKeystore() + keystore = apk_utils.default_keystore() with utils.TempDir() as temp: processed_apk = None if dex:
diff --git a/tools/apk_utils.py b/tools/apk_utils.py index c3c616b..86d3b0f 100755 --- a/tools/apk_utils.py +++ b/tools/apk_utils.py
@@ -5,9 +5,13 @@ import optparse import os +import shutil import subprocess import sys +import time + import utils +import zip_utils USAGE = 'usage: %prog [options] <apk>' @@ -34,6 +38,30 @@ apk = args[0] return (options, apk) +def add_baseline_profile_to_apk(apk, baseline_profile, tmp_dir): + if baseline_profile is None: + return apk + ts = time.time_ns() + dest_apk = os.path.join(tmp_dir, 'app-%s.apk' % ts) + dest_apk_aligned = os.path.join(tmp_dir, 'app-aligned-%s.apk' % ts) + dest_apk_signed = os.path.join(tmp_dir, 'app-signed-%s.apk' % ts) + shutil.copy2(apk, dest_apk) + zip_utils.add_file_to_zip( + baseline_profile, 'assets/dexopt/baseline.prof', dest_apk) + align(dest_apk, dest_apk_aligned) + sign_with_apksigner(dest_apk_aligned, dest_apk_signed) + return dest_apk_signed + +def align(apk, aligned_apk): + zipalign_path = ( + 'zipalign' if 'build_tools' in os.environ.get('PATH') + else os.path.join(utils.getAndroidBuildTools(), 'zipalign')) + cmd = [zipalign_path, '-f', '4', apk, aligned_apk] + utils.RunCmd(cmd, quiet=True, logging=False) + return aligned_apk + +def default_keystore(): + return os.path.join(os.getenv('HOME'), '.android', 'app.keystore') def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True): utils.Print('Signing (ignore the warnings)', quiet=quiet) @@ -52,20 +80,20 @@ utils.RunCmd(cmd, quiet=quiet) def sign_with_apksigner( - unsigned_apk, signed_apk, keystore, password='android', quiet=False, + unsigned_apk, signed_apk, keystore=None, password='android', quiet=False, logging=True): cmd = [ os.path.join(utils.getAndroidBuildTools(), 'apksigner'), 'sign', '-v', - '--ks', keystore, + '--ks', keystore or default_keystore(), '--ks-pass', 'pass:' + password, '--min-sdk-version', '19', '--out', signed_apk, unsigned_apk ] utils.RunCmd(cmd, quiet=quiet, logging=logging) - + return signed_apk def main(): (options, apk) = parse_options()
diff --git a/tools/archive.py b/tools/archive.py index 893fc19..d7bf1a9 100755 --- a/tools/archive.py +++ b/tools/archive.py
@@ -33,22 +33,13 @@ type="string", action="store") return result.parse_args() -def GetToolVersion(jar_path): - # TODO(mkroghj) This would not work for r8-lib, maybe use utils.getR8Version. - output = subprocess.check_output([ - jdk.GetJavaExecutable(), '-jar', jar_path, '--version' - ]).decode('utf-8') - return output.splitlines()[0].strip() - def GetVersion(): - r8_version = GetToolVersion(utils.R8_JAR) - d8_version = GetToolVersion(utils.D8_JAR) - # The version printed is "D8 vVERSION_NUMBER" and "R8 vVERSION_NUMBER" - # Sanity check that versions match. - if d8_version.split()[1] != r8_version.split()[1]: - raise Exception( - 'Version mismatch: \n%s\n%s' % (d8_version, r8_version)) - return d8_version.split()[1] + output = subprocess.check_output([ + jdk.GetJavaExecutable(), '-cp', utils.R8_JAR, 'com.android.tools.r8.R8', + '--version' + ]).decode('utf-8') + r8_version = output.splitlines()[0].strip() + return r8_version.split()[1] def GetGitBranches(): return subprocess.check_output(['git', 'show', '-s', '--pretty=%d', 'HEAD']) @@ -157,7 +148,6 @@ # The '-Pno_internal' flag is important because we generate the lib based on uses in tests. gradle.RunGradle([ utils.R8, - utils.D8, utils.R8LIB, utils.R8LIB_NO_DEPS, utils.R8RETRACE, @@ -193,7 +183,6 @@ create_maven_release.write_default_r8_pom_file(default_pom_file, version) for file in [ - utils.D8_JAR, utils.R8_JAR, utils.R8LIB_JAR, utils.R8LIB_JAR + '.map',
diff --git a/tools/dexsplitter.py b/tools/dexsplitter.py deleted file mode 100755 index a97d089..0000000 --- a/tools/dexsplitter.py +++ /dev/null
@@ -1,10 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2018, 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. - -import sys -import toolhelper - -if __name__ == '__main__': - sys.exit(toolhelper.run('dexsplitter', sys.argv[1:]))
diff --git a/tools/gradle.py b/tools/gradle.py index 19fa773..92503a7 100755 --- a/tools/gradle.py +++ b/tools/gradle.py
@@ -59,10 +59,10 @@ GRADLE, GRADLE_TGZ, GRADLE_SHA1, 'Gradle binary') def EnsureJdk(): - jdkHome = jdk.GetJdkHome() - jdkTgz = jdkHome + '.tar.gz' + jdkRoot = jdk.GetJdkRoot() + jdkTgz = jdkRoot + '.tar.gz' jdkSha1 = jdkTgz + '.sha1' - utils.EnsureDepFromGoogleCloudStorage(jdkHome, jdkTgz, jdkSha1, 'JDK') + utils.EnsureDepFromGoogleCloudStorage(jdkRoot, jdkTgz, jdkSha1, 'JDK') def EnsureDeps(): EnsureGradle()
diff --git a/tools/internal_test.py b/tools/internal_test.py index 8e4caa5..45dd694 100755 --- a/tools/internal_test.py +++ b/tools/internal_test.py
@@ -189,7 +189,7 @@ return utils.get_HEAD_sha1() def get_test_result_dir(): - return os.path.join(utils.R8_TEST_RESULTS_BUCKET, TEST_RESULT_DIR) + return os.path.join(utils.R8_INTERNAL_TEST_RESULTS_BUCKET, TEST_RESULT_DIR) def get_sha_destination(sha): return os.path.join(get_test_result_dir(), sha) @@ -287,6 +287,9 @@ print('Tests failed, you can print the logs by running(googlers only):') print(' tools/internal_test.py --print_logs %s' % git_hash) return 1 + else: + print(' Test validation of archiving logs, see b/177799191') + print(' tools/internal_test.py --print_logs %s' % git_hash) def run_continuously(): # If this script changes, we will restart ourselves @@ -374,7 +377,7 @@ stderr_fd.close() if stdout_fd: stdout_fd.close() - if exitcode != 0: + if exitcode != 0 or True: handle_output(archive, stderr, stdout, popen.returncode, timed_out, ' '.join(cmd)) return exitcode
diff --git a/tools/jdk.py b/tools/jdk.py index c9cb628..5cf1465 100755 --- a/tools/jdk.py +++ b/tools/jdk.py
@@ -13,7 +13,10 @@ def GetJdkHome(): return GetJdk11Home() -def GetJdk11Home(): +def GetJdkRoot(): + return GetJdk11Root() + +def GetJdk11Root(): root = os.path.join(JDK_DIR, 'jdk-11') if defines.IsLinux(): return os.path.join(root, 'linux') @@ -24,6 +27,14 @@ else: return os.environ['JAVA_HOME'] +def GetJdk11Home(): + root = GetJdk11Root() + # osx has the home inside Contents/Home in the bundle + if defines.IsOsX(): + return os.path.join(root,'Contents', 'Home') + else: + return root + def GetJdk9Home(): root = os.path.join(JDK_DIR, 'openjdk-9.0.4') if defines.IsLinux():
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions index fee679c..02e81cd 100644 --- a/tools/linux/README.art-versions +++ b/tools/linux/README.art-versions
@@ -42,9 +42,9 @@ <continue with repo sync as above> -art-13-master (Android T) -------------------------- -Build from master commit e208b04cc2efaf707390d7acbe8f978142701d72. +art-13 (Android T) +------------------ +Build from tm-dev commit 442e1091f39417c692d91609af05e58af60d8e2b. repo sync -cq -j24 source build/envsetup.sh @@ -53,16 +53,16 @@ m -j48 build-art m -j48 test-art-host -Collected into tools/linux/host/art-13-master. The "host" path element is checked +Collected into tools/linux/host/art-13. The "host" path element is checked by the script for running Art. cd <r8 checkout> scripts/update-host-art.sh \ - --android-checkout <...>/android/master \ - --art-dir host/art-13-master \ + --android-checkout <...>/android/tm-dev \ + --art-dir host/art-13 \ --android-product redfin -(cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-13-master) +(cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-13) art-12.0.0 (Android S) ---------------------
diff --git a/tools/linux/host/art-13-dev.tar.gz.sha1 b/tools/linux/host/art-13-dev.tar.gz.sha1 new file mode 100644 index 0000000..5367655 --- /dev/null +++ b/tools/linux/host/art-13-dev.tar.gz.sha1
@@ -0,0 +1 @@ +fed34a1eecaf012550cdd9df24434b8b2068a194 \ No newline at end of file
diff --git a/tools/linux/host/art-13-master.tar.gz.sha1 b/tools/linux/host/art-13-master.tar.gz.sha1 deleted file mode 100644 index 8b16edd..0000000 --- a/tools/linux/host/art-13-master.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -a2cf2b34b8712adb5c25a18bc6135946cfb1a047 \ No newline at end of file
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py index 981bcbd..7f1d3bc 100644 --- a/tools/startup/adb_utils.py +++ b/tools/startup/adb_utils.py
@@ -4,11 +4,33 @@ # BSD-style license that can be found in the LICENSE file. from enum import Enum +import os import subprocess +import sys +import threading import time +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import utils + DEVNULL=subprocess.DEVNULL +class ProcessReader(threading.Thread): + + def __init__(self, process): + threading.Thread.__init__(self) + self.lines = [] + self.process = process + + def run(self): + for line in self.process.stdout: + line = line.decode('utf-8').strip() + self.lines.append(line) + + def stop(self): + self.process.kill() + class ScreenState(Enum): OFF_LOCKED = 1, OFF_UNLOCKED = 2 @@ -27,6 +49,11 @@ def is_on_and_unlocked(self): return self == ScreenState.ON_UNLOCKED +def broadcast(action, component, device_id=None): + print('Sending broadcast %s' % action) + cmd = create_adb_cmd('shell am broadcast -a %s %s' % (action, component), device_id) + return subprocess.check_output(cmd).decode('utf-8').strip().splitlines() + def create_adb_cmd(arguments, device_id=None): assert isinstance(arguments, list) or isinstance(arguments, str) cmd = ['adb'] @@ -39,7 +66,7 @@ def capture_app_profile_data(app_id, device_id=None): cmd = create_adb_cmd( 'shell killall -s SIGUSR1 %s' % app_id, device_id) - subprocess.check_output(cmd) + subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) time.sleep(5) def check_app_has_profile_data(app_id, device_id=None): @@ -54,6 +81,10 @@ if size == 4: raise ValueError('Expected size of profile at %s to be > 4K' % profile_path) +def clear_logcat(device_id=None): + cmd = create_adb_cmd('logcat -c', device_id) + subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) + def clear_profile_data(app_id, device_id=None): cmd = create_adb_cmd( 'shell cmd package compile --reset %s' % app_id, device_id) @@ -65,11 +96,13 @@ subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) def force_compilation(app_id, device_id=None): + print('Applying AOT (full)') cmd = create_adb_cmd( 'shell cmd package compile -m speed -f %s' % app_id, device_id) subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) def force_profile_compilation(app_id, device_id=None): + print('Applying AOT (profile)') cmd = create_adb_cmd( 'shell cmd package compile -m speed-profile -f %s' % app_id, device_id) subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) @@ -86,6 +119,15 @@ 'Expected stdout to end with ".apk", was: %s' % stdout) return apk_path +def get_profile_data(app_id, device_id=None): + with utils.TempDir() as temp: + source = get_profile_path(app_id) + target = os.path.join(temp, 'primary.prof') + cmd = create_adb_cmd('pull %s %s' % (source, target), device_id) + subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) + with open(target, 'rb') as f: + return f.read() + def get_profile_path(app_id): return '/data/misc/profiles/cur/0/%s/primary.prof' % app_id @@ -170,10 +212,23 @@ return screen_off_timeout def install(apk, device_id=None): + print('Installing %s' % apk) cmd = create_adb_cmd('install %s' % apk, device_id) stdout = subprocess.check_output(cmd).decode('utf-8') assert 'Success' in stdout +def install_profile(app_id, device_id=None): + # This assumes that the profileinstaller library has been added to the app, + # https://developer.android.com/jetpack/androidx/releases/profileinstaller. + action = 'androidx.profileinstaller.action.INSTALL_PROFILE' + component = '%s/androidx.profileinstaller.ProfileInstallReceiver' % app_id + stdout = broadcast(action, component, device_id) + assert len(stdout) == 2 + assert stdout[0] == ('Broadcasting: Intent { act=%s flg=0x400000 cmp=%s }' % (action, component)) + assert stdout[1] == 'Broadcast completed: result=1', stdout[1] + stop_app(app_id, device_id) + force_profile_compilation(app_id, device_id) + def issue_key_event(key_event, device_id=None, sleep_in_seconds=1): cmd = create_adb_cmd('shell input keyevent %s' % key_event, device_id) stdout = subprocess.check_output(cmd).decode('utf-8').strip() @@ -200,6 +255,10 @@ assert not wait_for_activity_to_launch or 'total_time' in result return result +def navigate_to_home_screen(device_id=None): + cmd = create_adb_cmd('shell input keyevent KEYCODE_HOME', device_id) + subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) + def prepare_for_interaction_with_device(device_id=None, device_pin=None): # Increase screen off timeout to avoid device screen turns off. twenty_four_hours_in_millis = 24 * 60 * 60 * 1000 @@ -226,7 +285,26 @@ stdout = subprocess.check_output(cmd).decode('utf-8').strip() assert len(stdout) == 0 +def start_logcat(device_id=None, format=None, filter=None): + args = ['logcat'] + if format: + args.extend(['--format', format]) + if filter: + args.append(filter) + cmd = create_adb_cmd(args, device_id) + logcat_process = subprocess.Popen( + cmd, bufsize=1024*1024, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + reader = ProcessReader(logcat_process) + reader.start() + return reader + +def stop_logcat(logcat_reader): + logcat_reader.stop() + logcat_reader.join() + return logcat_reader.lines + def stop_app(app_id, device_id=None): + print('Shutting down %s' % app_id) cmd = create_adb_cmd('shell am force-stop %s' % app_id, device_id) subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL) @@ -237,6 +315,7 @@ device_id) def uninstall(app_id, device_id=None): + print('Uninstalling %s' % app_id) cmd = create_adb_cmd('uninstall %s' % app_id, device_id) process_result = subprocess.run(cmd, capture_output=True) stdout = process_result.stdout.decode('utf-8') @@ -246,7 +325,8 @@ else: expected_error = ( 'java.lang.IllegalArgumentException: Unknown package: %s' % app_id) - assert expected_error in stderr + assert 'Failure [DELETE_FAILED_INTERNAL_ERROR]' in stdout \ + or expected_error in stderr def unlock(device_id=None, device_pin=None): screen_state = get_screen_state(device_id)
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py index ea0fdf3..f1faee43 100755 --- a/tools/startup/generate_startup_descriptors.py +++ b/tools/startup/generate_startup_descriptors.py
@@ -6,16 +6,23 @@ import adb_utils import argparse +import os import sys import time def extend_startup_descriptors(startup_descriptors, iteration, options): - generate_startup_profile_on_device(options) - classes_and_methods = adb_utils.get_classes_and_methods_from_app_profile( - options.app_id, options.device_id) - current_startup_descriptors = \ - transform_classes_and_methods_to_r8_startup_descriptors( - classes_and_methods, options) + (logcat, profile, profile_classes_and_methods) = \ + generate_startup_profile(options) + if options.logcat: + write_tmp_logcat(logcat, iteration, options) + current_startup_descriptors = get_r8_startup_descriptors_from_logcat(logcat) + else: + write_tmp_profile(profile, iteration, options) + write_tmp_profile_classes_and_methods(profile_classes_and_methods, iteration, options) + current_startup_descriptors = \ + transform_classes_and_methods_to_r8_startup_descriptors( + profile_classes_and_methods, options) + write_tmp_startup_descriptors(current_startup_descriptors, iteration, options) number_of_new_startup_descriptors = add_r8_startup_descriptors( startup_descriptors, current_startup_descriptors) if options.out is not None: @@ -24,15 +31,32 @@ % (number_of_new_startup_descriptors, iteration + 1)) return number_of_new_startup_descriptors -def generate_startup_profile_on_device(options): - if not options.use_existing_profile: - # Clear existing profile data. - adb_utils.clear_profile_data(options.app_id, options.device_id) - +def generate_startup_profile(options): + logcat = None + profile = None + profile_classes_and_methods = None + if options.use_existing_profile: + # Verify presence of profile. + adb_utils.check_app_has_profile_data(options.app_id, options.device_id) + profile = adb_utils.get_profile_data(options.app_id, options.device_id) + profile_classes_and_methods = \ + adb_utils.get_classes_and_methods_from_app_profile( + options.app_id, options.device_id) + else: # Unlock device. tear_down_options = adb_utils.prepare_for_interaction_with_device( options.device_id, options.device_pin) + logcat_process = None + if options.logcat: + # Clear logcat and start capturing logcat. + adb_utils.clear_logcat(options.device_id) + logcat_process = adb_utils.start_logcat( + options.device_id, format='raw', filter='r8:I *:S') + else: + # Clear existing profile data. + adb_utils.clear_profile_data(options.app_id, options.device_id) + # Launch activity to generate startup profile on device. adb_utils.launch_activity( options.app_id, options.main_activity, options.device_id) @@ -40,17 +64,36 @@ # Wait for activity startup. time.sleep(options.startup_duration) - # Capture startup profile. - adb_utils.capture_app_profile_data(options.app_id, options.device_id) + if options.logcat: + # Get startup descriptors from logcat. + logcat = adb_utils.stop_logcat(logcat_process) + else: + # Capture startup profile. + adb_utils.capture_app_profile_data(options.app_id, options.device_id) + profile = adb_utils.get_profile_data(options.app_id, options.device_id) + profile_classes_and_methods = \ + adb_utils.get_classes_and_methods_from_app_profile( + options.app_id, options.device_id) # Shutdown app. adb_utils.stop_app(options.app_id, options.device_id) - adb_utils.tear_down_after_interaction_with_device( tear_down_options, options.device_id) - # Verify presence of profile. - adb_utils.check_app_has_profile_data(options.app_id, options.device_id) + return (logcat, profile, profile_classes_and_methods) + +def get_r8_startup_descriptors_from_logcat(logcat): + startup_descriptors = [] + for line in logcat: + if line == '--------- beginning of main': + continue + if line == '--------- beginning of system': + continue + if not line.startswith('L') or not line.endswith(';'): + print('Unrecognized line in logcat: %s' % line) + continue + startup_descriptors.append(line) + return startup_descriptors def transform_classes_and_methods_to_r8_startup_descriptors( classes_and_methods, options): @@ -72,6 +115,53 @@ return new_number_of_startup_descriptors \ - previous_number_of_startup_descriptors +def write_tmp_binary_artifact(artifact, iteration, options, name): + if not options.tmp_dir: + return + out_dir = os.path.join(options.tmp_dir, str(iteration)) + os.makedirs(out_dir, exist_ok=True) + path = os.path.join(out_dir, name) + with open(path, 'wb') as f: + f.write(artifact) + +def write_tmp_textual_artifact(artifact, iteration, options, name, item_to_string=None): + if not options.tmp_dir: + return + out_dir = os.path.join(options.tmp_dir, str(iteration)) + os.makedirs(out_dir, exist_ok=True) + path = os.path.join(out_dir, name) + with open(path, 'w') as f: + for item in artifact: + f.write(item if item_to_string is None else item_to_string(item)) + f.write('\n') + +def write_tmp_logcat(logcat, iteration, options): + write_tmp_textual_artifact(logcat, iteration, options, 'logcat.txt') + +def write_tmp_profile(profile, iteration, options): + write_tmp_binary_artifact(profile, iteration, options, 'primary.prof') + +def write_tmp_profile_classes_and_methods( + profile_classes_and_methods, iteration, options): + def item_to_string(item): + descriptor = item.get('descriptor') + flags = item.get('flags') + return '%s%s%s%s' % ( + 'H' if flags.get('hot') else '', + 'S' if flags.get('startup') else '', + 'P' if flags.get('post_startup') else '', + descriptor) + write_tmp_textual_artifact( + profile_classes_and_methods, + iteration, + options, + 'profile.txt', + item_to_string) + +def write_tmp_startup_descriptors(startup_descriptors, iteration, options): + write_tmp_textual_artifact( + startup_descriptors, iteration, options, 'startup-descriptors.txt') + def parse_options(argv): result = argparse.ArgumentParser( description='Generate a perfetto trace file.') @@ -84,6 +174,9 @@ help='Device id (e.g., emulator-5554).') result.add_argument('--device-pin', help='Device pin code (e.g., 1234)') + result.add_argument('--logcat', + action='store_true', + default=False) result.add_argument('--include-post-startup', help='Include post startup classes and methods in the R8 ' 'startup descriptors', @@ -102,6 +195,9 @@ help='Duration in seconds before shutting down app', default=15, type=int) + result.add_argument('--tmp-dir', + help='Directory where to store intermediate artifacts' + ' (by default these are not emitted)') result.add_argument('--until-stable', help='Repeat profile generation until no new startup ' 'descriptors are found',
diff --git a/tools/startup/measure_startup.py b/tools/startup/measure_startup.py index 080f827..f0b2ce9 100755 --- a/tools/startup/measure_startup.py +++ b/tools/startup/measure_startup.py
@@ -20,6 +20,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import adb_utils +import apk_utils import perfetto_utils import utils @@ -45,13 +46,13 @@ tear_down_options['previous_screen_off_timeout'], options.device_id) -def run_all(options, tmp_dir): +def run_all(apk, options, tmp_dir): # Launch app while collecting information. data_avg = {} for iteration in range(options.iterations): print('Starting iteration %i' % iteration) out_dir = os.path.join(options.out_dir, str(iteration)) - prepare_for_run(out_dir, options) + prepare_for_run(apk, out_dir, options) data = run(out_dir, options, tmp_dir) add_data(data_avg, data) print("Result:") @@ -64,20 +65,25 @@ print(data_avg) write_data(options.out_dir, data_avg) -def prepare_for_run(out_dir, options): +def prepare_for_run(apk, out_dir, options): adb_utils.root(options.device_id) adb_utils.uninstall(options.app_id, options.device_id) - adb_utils.install(options.apk, options.device_id) - adb_utils.clear_profile_data(options.app_id, options.device_id) + adb_utils.install(apk, options.device_id) if options.aot: - adb_utils.force_compilation(options.app_id, options.device_id) - elif options.aot_profile: + if options.baseline_profile: + adb_utils.clear_profile_data(options.app_id, options.device_id) + adb_utils.install_profile(options.app_id, options.device_id) + else: + adb_utils.force_compilation(options.app_id, options.device_id) + if options.hot_startup: adb_utils.launch_activity( - options.app_id, options.main_activity, options.device_id) - time.sleep(options.aot_profile_sleep) - adb_utils.stop_app(options.app_id, options.device_id) - adb_utils.force_profile_compilation(options.app_id, options.device_id) - + options.app_id, + options.main_activity, + options.device_id, + wait_for_activity_to_launch=False) + time.sleep(options.startup_duration) + adb_utils.navigate_to_home_screen(options.device_id) + time.sleep(1) adb_utils.drop_caches(options.device_id) os.makedirs(out_dir, exist_ok=True) @@ -87,9 +93,9 @@ # Start perfetto trace collector. perfetto_process = None perfetto_trace_path = None - if not options.no_perfetto: + if options.perfetto: perfetto_process, perfetto_trace_path = perfetto_utils.record_android_trace( - out_dir, tmp_dir) + out_dir, tmp_dir, options.device_id) # Launch main activity. launch_activity_result = adb_utils.launch_activity( @@ -99,7 +105,7 @@ wait_for_activity_to_launch=True) # Wait for perfetto trace collector to stop. - if not options.no_perfetto: + if options.perfetto: perfetto_utils.stop_record_android_trace(perfetto_process, out_dir) # Get minor and major page faults from app process. @@ -137,36 +143,38 @@ def compute_startup_data(launch_activity_result, perfetto_trace_path, options): startup_data = { - 'time_to_activity_started_ms': launch_activity_result.get('total_time') + 'adb_startup': launch_activity_result.get('total_time') } perfetto_startup_data = {} - if not options.no_perfetto: + if options.perfetto: trace_processor = TraceProcessor(file_path=perfetto_trace_path) - # Compute time to first frame according to the builtin android_startup metric. + # Compute time to first frame according to the builtin android_startup + # metric. startup_metric = trace_processor.metric(['android_startup']) time_to_first_frame_ms = \ startup_metric.android_startup.startup[0].to_first_frame.dur_ms + perfetto_startup_data['perfetto_startup'] = round(time_to_first_frame_ms) - # Compute time to first and last doFrame event. - bind_application_slice = perfetto_utils.find_unique_slice_by_name( - 'bindApplication', options, trace_processor) - activity_start_slice = perfetto_utils.find_unique_slice_by_name( - 'activityStart', options, trace_processor) - do_frame_slices = perfetto_utils.find_slices_by_name( - 'Choreographer#doFrame', options, trace_processor) - first_do_frame_slice = next(do_frame_slices) - *_, last_do_frame_slice = do_frame_slices + if not options.hot_startup: + # Compute time to first and last doFrame event. + bind_application_slice = perfetto_utils.find_unique_slice_by_name( + 'bindApplication', options, trace_processor) + activity_start_slice = perfetto_utils.find_unique_slice_by_name( + 'activityStart', options, trace_processor) + do_frame_slices = perfetto_utils.find_slices_by_name( + 'Choreographer#doFrame', options, trace_processor) + first_do_frame_slice = next(do_frame_slices) + *_, last_do_frame_slice = do_frame_slices - perfetto_startup_data = { - 'time_to_first_frame_ms': round(time_to_first_frame_ms), - 'time_to_first_choreographer_do_frame_ms': - round(perfetto_utils.get_slice_end_since_start( - first_do_frame_slice, bind_application_slice)), - 'time_to_last_choreographer_do_frame_ms': - round(perfetto_utils.get_slice_end_since_start( - last_do_frame_slice, bind_application_slice)) - } + perfetto_startup_data.update({ + 'time_to_first_choreographer_do_frame_ms': + round(perfetto_utils.get_slice_end_since_start( + first_do_frame_slice, bind_application_slice)), + 'time_to_last_choreographer_do_frame_ms': + round(perfetto_utils.get_slice_end_since_start( + last_do_frame_slice, bind_application_slice)) + }) # Return combined startup data. return startup_data | perfetto_startup_data @@ -191,10 +199,6 @@ help='Enable force compilation using profiles', default=False, action='store_true') - result.add_argument('--aot-profile-sleep', - help='Duration in seconds before forcing compilation', - default=15, - type=int) result.add_argument('--apk', help='Path to the APK', required=True) @@ -202,6 +206,10 @@ help='Device id (e.g., emulator-5554).') result.add_argument('--device-pin', help='Device pin code (e.g., 1234)') + result.add_argument('--hot-startup', + help='Measure hot startup instead of cold startup', + default=False, + action='store_true') result.add_argument('--iterations', help='Number of traces to generate', default=1, @@ -216,16 +224,26 @@ result.add_argument('--out-dir', help='Directory to store trace files in', required=True) + result.add_argument('--baseline-profile', + help='Baseline profile to install') + result.add_argument('--startup-duration', + help='Duration in seconds before shutting down app', + default=15, + type=int) options, args = result.parse_known_args(argv) - assert (not options.aot) or (not options.aot_profile) + setattr(options, 'perfetto', not options.no_perfetto) + # Profile is only used with --aot. + assert options.aot or not options.baseline_profile return options, args def main(argv): (options, args) = parse_options(argv) with utils.TempDir() as tmp_dir: + apk = apk_utils.add_baseline_profile_to_apk( + options.apk, options.baseline_profile, tmp_dir) tear_down_options = adb_utils.prepare_for_interaction_with_device( options.device_id, options.device_pin) - run_all(options, tmp_dir) + run_all(apk, options, tmp_dir) adb_utils.tear_down_after_interaction_with_device( tear_down_options, options.device_id)
diff --git a/tools/startup/perfetto_utils.py b/tools/startup/perfetto_utils.py index d85f53e..d2f53b4 100644 --- a/tools/startup/perfetto_utils.py +++ b/tools/startup/perfetto_utils.py
@@ -31,7 +31,7 @@ assert os.path.exists(record_android_trace_path) return record_android_trace_path -def record_android_trace(out_dir, tmp_dir): +def record_android_trace(out_dir, tmp_dir, device_id=None): record_android_trace_path = ensure_record_android_trace(tmp_dir) config_path = os.path.join(os.path.dirname(__file__), 'config.pbtx') perfetto_trace_path = os.path.join(out_dir, 'trace.perfetto-trace') @@ -43,6 +43,8 @@ '--out', perfetto_trace_path, '--no-open'] + if device_id is not None: + cmd.extend(['--serial', device_id]) perfetto_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) lines = []
diff --git a/tools/test.py b/tools/test.py index 665ab5f..0e3bf0f 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -530,7 +530,7 @@ print("Reading failed tests in", report) failing = set() inFailedSection = False - for line in file(report): + for line in open(report): l = line.strip() if l == "<h2>Failed tests</h2>": inFailedSection = True
diff --git a/tools/utils.py b/tools/utils.py index 2a4b18e..9e837c3 100644 --- a/tools/utils.py +++ b/tools/utils.py
@@ -51,7 +51,6 @@ R8LIB_TESTS_DEPS_TARGET = R8_TESTS_DEPS_TARGET ALL_DEPS_JAR = os.path.join(LIBS, 'deps_all.jar') -D8_JAR = os.path.join(LIBS, 'd8.jar') R8_JAR = os.path.join(LIBS, 'r8.jar') R8_WITH_RELOCATED_DEPS_JAR = os.path.join(LIBS, 'r8_with_relocated_deps.jar') R8LIB_JAR = os.path.join(LIBS, 'r8lib.jar') @@ -102,6 +101,7 @@ USER_HOME = os.path.expanduser('~') R8_TEST_RESULTS_BUCKET = 'r8-test-results' +R8_INTERNAL_TEST_RESULTS_BUCKET = 'r8-internal-test-results' def archive_file(name, gs_dir, src_file): gs_file = '%s/%s' % (gs_dir, name) @@ -344,7 +344,9 @@ if is_html: cmd += ['-z', 'html'] if public_read: - cmd += ['-a', 'public-read'] + # TODO(b/177799191) Temporarily disable public-read to test uniform access control + if 'r8-test-results' not in destination: + cmd += ['-a', 'public-read'] cmd += ['-R', directory, destination] PrintCmd(cmd) subprocess.check_call(cmd)
diff --git a/tools/zip_utils.py b/tools/zip_utils.py new file mode 100644 index 0000000..b0571f2 --- /dev/null +++ b/tools/zip_utils.py
@@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# 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. + +import zipfile + +def add_file_to_zip(file, destination, zip_file): + with zipfile.ZipFile(zip_file, 'a') as zip: + zip.write(file, destination)