Merge commit 'e19234b32da0a3a33c6677979323e27e047edf91' into dev-release
diff --git a/src/library_desugar/java/j$/nio/file/FileSystem.java b/src/library_desugar/java/j$/nio/file/FileSystem.java
new file mode 100644
index 0000000..a7a49a2
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/FileSystem.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 j$.nio.file;
+
+import j$.nio.file.spi.FileSystemProvider;
+
+public class FileSystem {
+ public FileSystemProvider provider() {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/FileSystems.java b/src/library_desugar/java/j$/nio/file/FileSystems.java
new file mode 100644
index 0000000..d7214ca
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/FileSystems.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 j$.nio.file;
+
+public class FileSystems {
+ public static FileSystem getDefault() {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/Files.java b/src/library_desugar/java/j$/nio/file/Files.java
index 2825d24..f34593b 100644
--- a/src/library_desugar/java/j$/nio/file/Files.java
+++ b/src/library_desugar/java/j$/nio/file/Files.java
@@ -5,10 +5,9 @@
package j$.nio.file;
import java.io.IOException;
-import java.nio.file.Path;
public class Files {
- public static String probeContentType(Path path) throws IOException {
+ public static String probeContentType(j$.nio.file.Path path) throws IOException {
return null;
}
}
diff --git a/src/library_desugar/java/j$/nio/file/LinkOption.java b/src/library_desugar/java/j$/nio/file/LinkOption.java
new file mode 100644
index 0000000..b8cf168
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/LinkOption.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.nio.file;
+
+public class LinkOption extends OpenOption {
+ public static java.nio.file.LinkOption wrap_convert(j$.nio.file.LinkOption option) {
+ return null;
+ }
+
+ public static j$.nio.file.LinkOption wrap_convert(java.nio.file.LinkOption option) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/OpenOption.java b/src/library_desugar/java/j$/nio/file/OpenOption.java
new file mode 100644
index 0000000..8b063d1
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/OpenOption.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.nio.file;
+
+public class OpenOption {}
diff --git a/src/library_desugar/java/j$/nio/file/Path.java b/src/library_desugar/java/j$/nio/file/Path.java
new file mode 100644
index 0000000..781ab5f
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/Path.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.nio.file;
+
+public class Path {
+
+ public static j$.nio.file.Path wrap_convert(java.nio.file.Path path) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/StandardOpenOption.java b/src/library_desugar/java/j$/nio/file/StandardOpenOption.java
new file mode 100644
index 0000000..a65da37
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/StandardOpenOption.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 j$.nio.file;
+
+public class StandardOpenOption extends OpenOption {
+
+ public static java.nio.file.StandardOpenOption wrap_convert(
+ j$.nio.file.StandardOpenOption option) {
+ return null;
+ }
+
+ public static j$.nio.file.StandardOpenOption wrap_convert(
+ java.nio.file.StandardOpenOption option) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/WatchEvent.java b/src/library_desugar/java/j$/nio/file/WatchEvent.java
new file mode 100644
index 0000000..fd977e9
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/WatchEvent.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.nio.file;
+
+public class WatchEvent<T> {
+
+ public static java.nio.file.WatchEvent<?> wrap_convert(j$.nio.file.WatchEvent<?> option) {
+ return null;
+ }
+
+ public static j$.nio.file.WatchEvent<?> wrap_convert(java.nio.file.WatchEvent<?> option) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributeView.java b/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributeView.java
new file mode 100644
index 0000000..774cf2c
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributeView.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class BasicFileAttributeView extends FileAttributeView {
+ public static java.nio.file.attribute.BasicFileAttributeView wrap_convert(
+ j$.nio.file.attribute.BasicFileAttributeView fileAttributeView) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.BasicFileAttributeView wrap_convert(
+ java.nio.file.attribute.BasicFileAttributeView fileAttributeView) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributes.java b/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributes.java
new file mode 100644
index 0000000..dd91a95
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/BasicFileAttributes.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class BasicFileAttributes {
+ public static java.nio.file.attribute.BasicFileAttributes wrap_convert(
+ j$.nio.file.attribute.BasicFileAttributes fileAttributes) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.BasicFileAttributes wrap_convert(
+ java.nio.file.attribute.BasicFileAttributes fileAttributes) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/FileAttributeView.java b/src/library_desugar/java/j$/nio/file/attribute/FileAttributeView.java
new file mode 100644
index 0000000..34046eb3
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/FileAttributeView.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class FileAttributeView {
+ public static java.nio.file.attribute.FileAttributeView wrap_convert(
+ j$.nio.file.attribute.FileAttributeView fileAttributeView) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.FileAttributeView wrap_convert(
+ java.nio.file.attribute.FileAttributeView fileAttributeView) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/FileOwnerAttributeView.java b/src/library_desugar/java/j$/nio/file/attribute/FileOwnerAttributeView.java
new file mode 100644
index 0000000..34a637d
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/FileOwnerAttributeView.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class FileOwnerAttributeView extends FileAttributeView {
+ public static java.nio.file.attribute.FileOwnerAttributeView wrap_convert(
+ j$.nio.file.attribute.FileOwnerAttributeView fileAttributeView) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.FileOwnerAttributeView wrap_convert(
+ java.nio.file.attribute.FileOwnerAttributeView fileAttributeView) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributeView.java b/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributeView.java
new file mode 100644
index 0000000..bb24cb7
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributeView.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class PosixFileAttributeView extends FileAttributeView {
+ public static java.nio.file.attribute.PosixFileAttributeView wrap_convert(
+ j$.nio.file.attribute.PosixFileAttributeView fileAttributeView) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.PosixFileAttributeView wrap_convert(
+ java.nio.file.attribute.PosixFileAttributeView fileAttributeView) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributes.java b/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributes.java
new file mode 100644
index 0000000..5e61814
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/PosixFileAttributes.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class PosixFileAttributes extends BasicFileAttributes {
+ public static java.nio.file.attribute.PosixFileAttributes wrap_convert(
+ j$.nio.file.attribute.PosixFileAttributes fileAttributes) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.PosixFileAttributes wrap_convert(
+ java.nio.file.attribute.PosixFileAttributes fileAttributes) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/attribute/PosixFilePermission.java b/src/library_desugar/java/j$/nio/file/attribute/PosixFilePermission.java
new file mode 100644
index 0000000..94dbcbf
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/attribute/PosixFilePermission.java
@@ -0,0 +1,17 @@
+// 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.attribute;
+
+public class PosixFilePermission {
+ public static java.nio.file.attribute.PosixFilePermission wrap_convert(
+ j$.nio.file.attribute.PosixFilePermission permission) {
+ return null;
+ }
+
+ public static j$.nio.file.attribute.PosixFilePermission wrap_convert(
+ java.nio.file.attribute.PosixFilePermission permission) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
new file mode 100644
index 0000000..38152b3
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
@@ -0,0 +1,12 @@
+// 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.spi;
+
+public class FileSystemProvider {
+ public static java.nio.file.spi.FileSystemProvider wrap_convert(FileSystemProvider provider) {
+ // Rewritten in ASM to the wrapper method.
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/util/stream/Collector.java b/src/library_desugar/java/j$/util/stream/Collector.java
new file mode 100644
index 0000000..126d6ad
--- /dev/null
+++ b/src/library_desugar/java/j$/util/stream/Collector.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 j$.util.stream;
+
+public class Collector {
+ public enum Characteristics {
+ ;
+
+ public static java.util.stream.Collector.Characteristics wrap_convert(
+ j$.util.stream.Collector.Characteristics option) {
+ return null;
+ }
+
+ public static j$.util.stream.Collector.Characteristics wrap_convert(
+ java.util.stream.Collector.Characteristics option) {
+ return null;
+ }
+ }
+}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
index 658c351..d02344d 100644
--- a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
+++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -4,13 +4,12 @@
package java.adapter;
-import android.os.Build.VERSION;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import desugar.sun.nio.fs.DesugarDefaultFileSystemProvider;
+import j$.nio.file.FileSystems;
import java.net.URI;
import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
import java.nio.file.spi.FileSystemProvider;
/**
@@ -23,20 +22,26 @@
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();
+ try {
+ // On API 26 and above, FileSystems is present.
+ Class.forName("java.nio.file.FileSystems");
+ j$.nio.file.FileSystem fileSystem = FileSystems.getDefault();
+ j$.nio.file.spi.FileSystemProvider provider = fileSystem.provider();
+ return j$.nio.file.spi.FileSystemProvider.wrap_convert(provider);
+ } catch (ClassNotFoundException ignored) {
+ // We reach this path is API < 26.
}
+ // The DesugarDefaultFileSystemProvider requires the ThreadPolicy to be set to work correctly.
+ // We cannot set the ThreadPolicy in headless and it should not matter.
+ // In headless, android.os is absent so the following line will throw.
+ // In headfull, android.os is present and we set the thread policy.
+ try {
+ Class.forName("android.os.Build");
+ setThreadPolicy();
+ } catch (ClassNotFoundException ignored) {
+ // Headless mode.
+ }
+ return DesugarDefaultFileSystemProvider.instance();
}
private static void setThreadPolicy() {
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
index 1caf2f1..da93736 100644
--- a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
+++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -4,7 +4,6 @@
package java.adapter;
-import android.os.Build.VERSION;
import desugar.sun.nio.fs.DesugarDefaultFileTypeDetector;
import java.io.IOException;
import java.nio.file.Path;
@@ -18,18 +17,19 @@
private HybridFileTypeDetector() {}
public static FileTypeDetector create() {
- if (VERSION.SDK_INT >= 26) {
+ try {
+ // On API 26 and above, java.nio.file.Files is present.
+ Class.forName("java.nio.file.Files");
return new PlatformFileTypeDetector();
- } else {
+ } catch (ClassNotFoundException ignored) {
return DesugarDefaultFileTypeDetector.create();
}
}
- static class PlatformFileTypeDetector extends java.nio.file.spi.FileTypeDetector {
+ static class PlatformFileTypeDetector extends FileTypeDetector {
@Override
public String probeContentType(Path path) throws IOException {
- // Relies at runtime on java.nio.file.Files.
- return j$.nio.file.Files.probeContentType(path);
+ return j$.nio.file.Files.probeContentType(j$.nio.file.Path.wrap_convert(path));
}
}
}
diff --git a/src/library_desugar/java/java/nio/file/FileApiFlips.java b/src/library_desugar/java/java/nio/file/FileApiFlips.java
new file mode 100644
index 0000000..71b172a
--- /dev/null
+++ b/src/library_desugar/java/java/nio/file/FileApiFlips.java
@@ -0,0 +1,207 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package java.nio.file;
+
+import static java.util.ConversionRuntimeException.exception;
+
+import java.nio.file.attribute.FileAttributeConversions;
+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 FileApiFlips {
+
+ public static Class<?> flipFileAttributes(Class<?> attributesClass) {
+ if (attributesClass == null) {
+ return null;
+ }
+ if (attributesClass == j$.nio.file.attribute.BasicFileAttributes.class) {
+ return java.nio.file.attribute.BasicFileAttributes.class;
+ }
+ if (attributesClass == j$.nio.file.attribute.PosixFileAttributes.class) {
+ return java.nio.file.attribute.PosixFileAttributes.class;
+ }
+ if (attributesClass == java.nio.file.attribute.BasicFileAttributes.class) {
+ return j$.nio.file.attribute.BasicFileAttributes.class;
+ }
+ if (attributesClass == java.nio.file.attribute.PosixFileAttributes.class) {
+ return j$.nio.file.attribute.PosixFileAttributes.class;
+ }
+ throw exception("java.nio.file.attribute.BasicFileAttributes", attributesClass);
+ }
+
+ public static Class<?> flipFileAttributeView(Class<?> attributeView) {
+ if (attributeView == null) {
+ return null;
+ }
+ if (attributeView == j$.nio.file.attribute.BasicFileAttributeView.class) {
+ return java.nio.file.attribute.BasicFileAttributeView.class;
+ }
+ if (attributeView == j$.nio.file.attribute.PosixFileAttributeView.class) {
+ return java.nio.file.attribute.PosixFileAttributeView.class;
+ }
+ if (attributeView == j$.nio.file.attribute.FileOwnerAttributeView.class) {
+ return java.nio.file.attribute.FileOwnerAttributeView.class;
+ }
+ if (attributeView == java.nio.file.attribute.BasicFileAttributeView.class) {
+ return j$.nio.file.attribute.BasicFileAttributeView.class;
+ }
+ if (attributeView == java.nio.file.attribute.PosixFileAttributeView.class) {
+ return j$.nio.file.attribute.PosixFileAttributeView.class;
+ }
+ if (attributeView == java.nio.file.attribute.FileOwnerAttributeView.class) {
+ return j$.nio.file.attribute.FileOwnerAttributeView.class;
+ }
+ throw exception("java.nio.file.attribute.FileAttributeView", attributeView);
+ }
+
+ public static RuntimeException exceptionOpenOption(Object suffix) {
+ throw exception("java.nio.file.OpenOption", suffix);
+ }
+
+ public static Set<?> flipOpenOptionSet(Set<?> openOptionSet) {
+ if (openOptionSet == null || openOptionSet.isEmpty()) {
+ return openOptionSet;
+ }
+ HashSet<Object> convertedSet = new HashSet<>();
+ Object guineaPig = openOptionSet.iterator().next();
+ if (guineaPig instanceof java.nio.file.OpenOption) {
+ for (Object item : openOptionSet) {
+ java.nio.file.OpenOption option;
+ try {
+ option = (java.nio.file.OpenOption) item;
+ } catch (ClassCastException cce) {
+ throw exceptionOpenOption(cce);
+ }
+ convertedSet.add(OpenOptionConversions.convert(option));
+ }
+ return convertedSet;
+ }
+ if (guineaPig instanceof j$.nio.file.OpenOption) {
+ for (Object item : openOptionSet) {
+ j$.nio.file.OpenOption option;
+ try {
+ option = (j$.nio.file.OpenOption) item;
+ } catch (ClassCastException cce) {
+ throw exceptionOpenOption(cce);
+ }
+ convertedSet.add(OpenOptionConversions.convert(option));
+ }
+ return convertedSet;
+ }
+ throw exceptionOpenOption(guineaPig.getClass());
+ }
+
+ public static RuntimeException exceptionFileTime(Object suffix) {
+ throw exception("java.nio.file.attribute.FileTime", suffix);
+ }
+
+ public static Map<String, Object> flipMapWithMaybeFileTimeValues(Map<String, Object> in) {
+ if (in == null || in.isEmpty()) {
+ return in;
+ }
+ HashMap<String, Object> newMap = new HashMap<>();
+ for (String key : in.keySet()) {
+ Object val = in.get(key);
+ if (val instanceof j$.nio.file.attribute.FileTime) {
+ j$.nio.file.attribute.FileTime fileTime;
+ try {
+ fileTime = (j$.nio.file.attribute.FileTime) val;
+ } catch (ClassCastException cce) {
+ throw exceptionFileTime(cce);
+ }
+ newMap.put(key, FileAttributeConversions.convert(fileTime));
+ } else if (val instanceof java.nio.file.attribute.FileTime) {
+ java.nio.file.attribute.FileTime fileTime;
+ try {
+ fileTime = (java.nio.file.attribute.FileTime) val;
+ } catch (ClassCastException cce) {
+ throw exceptionFileTime(cce);
+ }
+ newMap.put(key, FileAttributeConversions.convert(fileTime));
+ } else {
+ newMap.put(key, val);
+ }
+ }
+ return newMap;
+ }
+
+ public static RuntimeException exceptionPosixPermission(Object suffix) {
+ throw exception("java.nio.file.attribute.PosixFilePermission", suffix);
+ }
+
+ public static Set<?> flipPosixPermissionSet(Set<?> posixPermissions) {
+ if (posixPermissions == null || posixPermissions.isEmpty()) {
+ return posixPermissions;
+ }
+ HashSet<Object> convertedSet = new HashSet<>();
+ Object guineaPig = posixPermissions.iterator().next();
+ if (guineaPig instanceof java.nio.file.attribute.PosixFilePermission) {
+ for (Object item : posixPermissions) {
+ java.nio.file.attribute.PosixFilePermission permission;
+ try {
+ permission = (java.nio.file.attribute.PosixFilePermission) item;
+ } catch (ClassCastException cce) {
+ throw exceptionPosixPermission(cce);
+ }
+ convertedSet.add(j$.nio.file.attribute.PosixFilePermission.wrap_convert(permission));
+ }
+ return convertedSet;
+ }
+ if (guineaPig instanceof j$.nio.file.attribute.PosixFilePermission) {
+ for (Object item : posixPermissions) {
+ j$.nio.file.attribute.PosixFilePermission permission;
+ try {
+ permission = (j$.nio.file.attribute.PosixFilePermission) item;
+ } catch (ClassCastException cce) {
+ throw exceptionPosixPermission(cce);
+ }
+ convertedSet.add(j$.nio.file.attribute.PosixFilePermission.wrap_convert(permission));
+ }
+ return convertedSet;
+ }
+ throw exceptionPosixPermission(guineaPig.getClass());
+ }
+
+ public static RuntimeException exceptionWatchEvent(Object suffix) {
+ throw exception("java.nio.file.WatchEvent", suffix);
+ }
+
+ public static List<?> flipWatchEventList(List<?> watchEventList) {
+ if (watchEventList == null || watchEventList.isEmpty()) {
+ return watchEventList;
+ }
+ List<Object> convertedList = new ArrayList<>();
+ Object guineaPig = watchEventList.get(0);
+ if (guineaPig instanceof java.nio.file.WatchEvent) {
+ for (Object item : watchEventList) {
+ java.nio.file.WatchEvent<?> watchEvent;
+ try {
+ watchEvent = (java.nio.file.WatchEvent<?>) item;
+ } catch (ClassCastException cce) {
+ throw exceptionWatchEvent(cce);
+ }
+ convertedList.add(j$.nio.file.WatchEvent.wrap_convert(watchEvent));
+ }
+ return convertedList;
+ }
+ if (guineaPig instanceof j$.nio.file.WatchEvent) {
+ for (Object item : watchEventList) {
+ j$.nio.file.WatchEvent<?> watchEvent;
+ try {
+ watchEvent = (j$.nio.file.WatchEvent<?>) item;
+ } catch (ClassCastException cce) {
+ throw exceptionWatchEvent(cce);
+ }
+ convertedList.add(j$.nio.file.WatchEvent.wrap_convert(watchEvent));
+ }
+ return convertedList;
+ }
+ throw exceptionWatchEvent(guineaPig.getClass());
+ }
+}
diff --git a/src/library_desugar/java/java/nio/file/OpenOptionConversions.java b/src/library_desugar/java/java/nio/file/OpenOptionConversions.java
new file mode 100644
index 0000000..bfe4968
--- /dev/null
+++ b/src/library_desugar/java/java/nio/file/OpenOptionConversions.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.nio.file;
+
+import static java.util.ConversionRuntimeException.exception;
+
+public class OpenOptionConversions {
+ public static java.nio.file.OpenOption convert(j$.nio.file.OpenOption option) {
+ if (option == null) {
+ return null;
+ }
+ if (option instanceof j$.nio.file.StandardOpenOption) {
+ return j$.nio.file.StandardOpenOption.wrap_convert((j$.nio.file.StandardOpenOption) option);
+ }
+ if (option instanceof j$.nio.file.LinkOption) {
+ return j$.nio.file.LinkOption.wrap_convert((j$.nio.file.LinkOption) option);
+ }
+ throw exception("java.nio.file.OpenOption", option);
+ }
+
+ public static j$.nio.file.OpenOption convert(java.nio.file.OpenOption option) {
+ if (option == null) {
+ return null;
+ }
+ if (option instanceof java.nio.file.StandardOpenOption) {
+ return j$.nio.file.StandardOpenOption.wrap_convert((java.nio.file.StandardOpenOption) option);
+ }
+ if (option instanceof java.nio.file.LinkOption) {
+ return j$.nio.file.LinkOption.wrap_convert((java.nio.file.LinkOption) option);
+ }
+ throw exception("java.nio.file.OpenOption", option);
+ }
+}
diff --git a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
index f4c4beb..332c89b 100644
--- a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
+++ b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
@@ -4,7 +4,10 @@
package java.nio.file.attribute;
+import static java.util.ConversionRuntimeException.exception;
+
public class FileAttributeConversions {
+
public static java.nio.file.attribute.FileTime convert(j$.nio.file.attribute.FileTime fileTime) {
if (fileTime == null) {
return null;
@@ -18,4 +21,68 @@
}
return j$.nio.file.attribute.FileTime.fromMillis(fileTime.toMillis());
}
+
+ public static java.nio.file.attribute.FileAttributeView convert(
+ j$.nio.file.attribute.FileAttributeView fileAttributeView) {
+ if (fileAttributeView == null) {
+ return null;
+ }
+ if (fileAttributeView instanceof j$.nio.file.attribute.PosixFileAttributeView) {
+ return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
+ (j$.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
+ }
+ if (fileAttributeView instanceof j$.nio.file.attribute.FileOwnerAttributeView) {
+ return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
+ (j$.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
+ }
+ if (fileAttributeView instanceof j$.nio.file.attribute.BasicFileAttributeView) {
+ return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
+ (j$.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
+ }
+ throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
+ }
+
+ public static j$.nio.file.attribute.FileAttributeView convert(
+ java.nio.file.attribute.FileAttributeView fileAttributeView) {
+ if (fileAttributeView == null) {
+ return null;
+ }
+ if (fileAttributeView instanceof java.nio.file.attribute.PosixFileAttributeView) {
+ return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
+ (java.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
+ }
+ if (fileAttributeView instanceof java.nio.file.attribute.FileOwnerAttributeView) {
+ return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
+ (java.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
+ }
+ if (fileAttributeView instanceof java.nio.file.attribute.BasicFileAttributeView) {
+ return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
+ (java.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
+ }
+ throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
+ }
+
+ public static java.nio.file.attribute.BasicFileAttributes convert(
+ j$.nio.file.attribute.BasicFileAttributes fileAttributes) {
+ if (fileAttributes == null) {
+ return null;
+ }
+ if (fileAttributes instanceof j$.nio.file.attribute.PosixFileAttributes) {
+ return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
+ (j$.nio.file.attribute.PosixFileAttributes) fileAttributes);
+ }
+ return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
+ }
+
+ public static j$.nio.file.attribute.BasicFileAttributes convert(
+ java.nio.file.attribute.BasicFileAttributes fileAttributes) {
+ if (fileAttributes == null) {
+ return null;
+ }
+ if (fileAttributes instanceof java.nio.file.attribute.PosixFileAttributes) {
+ return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
+ (java.nio.file.attribute.PosixFileAttributes) fileAttributes);
+ }
+ return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
+ }
}
diff --git a/src/library_desugar/java/java/util/ConversionRuntimeException.java b/src/library_desugar/java/java/util/ConversionRuntimeException.java
new file mode 100644
index 0000000..896be1e
--- /dev/null
+++ b/src/library_desugar/java/java/util/ConversionRuntimeException.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 java.util;
+
+public class ConversionRuntimeException extends RuntimeException {
+
+ public ConversionRuntimeException(String message) {
+ super(message);
+ }
+
+ public static RuntimeException exception(String type, Object suffix) {
+ throw new ConversionRuntimeException("Unsupported " + type + " :" + suffix);
+ }
+}
diff --git a/src/library_desugar/java/java/util/stream/StreamApiFlips.java b/src/library_desugar/java/java/util/stream/StreamApiFlips.java
new file mode 100644
index 0000000..632c7bd
--- /dev/null
+++ b/src/library_desugar/java/java/util/stream/StreamApiFlips.java
@@ -0,0 +1,50 @@
+// 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.util.stream;
+
+import static java.util.ConversionRuntimeException.exception;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class StreamApiFlips {
+
+ public static RuntimeException exceptionCharacteristics(Object suffix) {
+ throw exception("java.util.stream.Collector.Characteristics", suffix);
+ }
+
+ public static Set<?> flipCharacteristicSet(Set<?> characteristicSet) {
+ if (characteristicSet == null || characteristicSet.isEmpty()) {
+ return characteristicSet;
+ }
+ HashSet<Object> convertedSet = new HashSet<>();
+ Object guineaPig = characteristicSet.iterator().next();
+ if (guineaPig instanceof java.util.stream.Collector.Characteristics) {
+ for (Object item : characteristicSet) {
+ java.util.stream.Collector.Characteristics characteristics;
+ try {
+ characteristics = (java.util.stream.Collector.Characteristics) item;
+ } catch (ClassCastException cce) {
+ throw exceptionCharacteristics(cce);
+ }
+ convertedSet.add(j$.util.stream.Collector.Characteristics.wrap_convert(characteristics));
+ }
+ return convertedSet;
+ }
+ if (guineaPig instanceof j$.util.stream.Collector.Characteristics) {
+ for (Object item : characteristicSet) {
+ j$.util.stream.Collector.Characteristics characteristics;
+ try {
+ characteristics = (j$.util.stream.Collector.Characteristics) item;
+ } catch (ClassCastException cce) {
+ throw exceptionCharacteristics(cce);
+ }
+ convertedSet.add(j$.util.stream.Collector.Characteristics.wrap_convert(characteristics));
+ }
+ return convertedSet;
+ }
+ throw exceptionCharacteristics(guineaPig.getClass());
+ }
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index ef9b604..c764f31 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -95,56 +95,24 @@
"java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
+ "api_generic_types_conversion": {
+ "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"]
+ },
"wrapper_conversion": [
- "java.util.function.IntUnaryOperator",
- "java.util.function.BiFunction",
- "java.util.function.IntConsumer",
- "java.util.function.IntBinaryOperator",
- "java.util.function.UnaryOperator",
- "java.util.function.DoubleConsumer",
- "java.util.function.IntPredicate",
- "java.util.Spliterator$OfLong",
- "java.util.stream.Collector",
- "java.util.function.LongPredicate",
- "java.util.function.ToLongFunction",
- "java.util.function.LongToDoubleFunction",
- "java.util.PrimitiveIterator$OfInt",
- "java.util.function.LongToIntFunction",
- "java.util.function.Predicate",
- "java.util.Spliterator$OfPrimitive",
- "java.util.function.DoubleToIntFunction",
- "java.util.function.ObjDoubleConsumer",
- "java.util.function.BinaryOperator",
- "java.util.stream.DoubleStream",
- "java.util.Spliterator$OfInt",
- "java.util.stream.Stream",
- "java.util.function.ObjLongConsumer",
- "java.util.function.ToDoubleFunction",
- "java.util.stream.IntStream",
- "java.util.function.LongBinaryOperator",
- "java.util.Spliterator$OfDouble",
- "java.util.function.DoubleFunction",
- "java.util.function.ObjIntConsumer",
- "java.util.function.Function",
- "java.util.function.Supplier",
- "java.util.function.DoubleUnaryOperator",
- "java.util.function.BiPredicate",
"java.util.PrimitiveIterator$OfDouble",
- "java.util.function.DoubleBinaryOperator",
+ "java.util.PrimitiveIterator$OfInt",
"java.util.PrimitiveIterator$OfLong",
- "java.util.function.BiConsumer",
- "java.util.function.IntFunction",
- "java.util.stream.LongStream",
- "java.util.function.IntToDoubleFunction",
- "java.util.function.LongFunction",
- "java.util.function.ToIntFunction",
- "java.util.function.LongConsumer",
- "java.util.function.Consumer",
- "java.util.function.IntToLongFunction",
- "java.util.function.DoubleToLongFunction",
- "java.util.function.LongUnaryOperator",
+ "java.util.Spliterator$OfDouble",
+ "java.util.Spliterator$OfInt",
+ "java.util.Spliterator$OfLong",
+ "java.util.Spliterator$OfPrimitive",
"java.util.stream.BaseStream",
- "java.util.function.DoublePredicate"
+ "java.util.stream.Collector",
+ "java.util.stream.Collector$Characteristics",
+ "java.util.stream.DoubleStream",
+ "java.util.stream.IntStream",
+ "java.util.stream.LongStream",
+ "java.util.stream.Stream"
],
"wrapper_conversion_excluding": {
"java.util.Spliterator": [
@@ -242,7 +210,8 @@
"java.util.KeyValueHolder": "j$.util.KeyValueHolder",
"java.util.SortedSet$1": "j$.util.SortedSet$1",
"java.util.Tripwire": "j$.util.Tripwire",
- "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
+ "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
+ "java.util.ConversionRuntimeException": "j$.util.ConversionRuntimeException"
},
"rewrite_derived_prefix": {
"java.util.DoubleSummaryStatistics": {
@@ -254,6 +223,9 @@
"java.util.LongSummaryStatistics": {
"j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics"
},
+ "java.util.stream.Collector": {
+ "j$.util.stream.Collector": "java.util.stream.Collector"
+ },
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index 4f0ec65..c09fa82 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -105,11 +105,16 @@
"retarget_method_with_emulated_dispatch": {
"java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
},
- "api_conversion_collection": {
- "java.nio.channels.AsynchronousFileChannel java.nio.file.spi.FileSystemProvider#newAsynchronousFileChannel(java.nio.file.Path, java.util.Set, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute[])" : [1, "OpenOption"],
- "java.nio.channels.SeekableByteChannel java.nio.file.spi.FileSystemProvider#newByteChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "OpenOption"],
- "java.nio.channels.FileChannel java.nio.file.spi.FileSystemProvider#newFileChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "OpenOption"],
- "java.util.List java.nio.file.WatchKey#pollEvents()": [-1, "java.nio.file.WatchEvent"]
+ "api_generic_types_conversion": {
+ "java.nio.channels.AsynchronousFileChannel java.nio.file.spi.FileSystemProvider#newAsynchronousFileChannel(java.nio.file.Path, java.util.Set, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"],
+ "java.nio.channels.SeekableByteChannel java.nio.file.spi.FileSystemProvider#newByteChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"],
+ "java.nio.channels.FileChannel java.nio.file.spi.FileSystemProvider#newFileChannel(java.nio.file.Path, java.util.Set, java.nio.file.attribute.FileAttribute[])" : [1, "java.util.Set java.nio.file.FileApiFlips#flipOpenOptionSet(java.util.Set)"],
+ "java.util.List java.nio.file.WatchKey#pollEvents()": [-1, "java.util.List java.nio.file.FileApiFlips#flipWatchEventList(java.util.List)"],
+ "java.nio.file.attribute.FileAttributeView java.nio.file.spi.FileSystemProvider#getFileAttributeView(java.nio.file.Path, java.lang.Class, java.nio.file.LinkOption[])": [1, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributeView(java.lang.Class)"],
+ "java.nio.file.attribute.BasicFileAttributes java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.Class, java.nio.file.LinkOption[])": [1, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributes(java.lang.Class)"],
+ "java.util.Set java.nio.file.attribute.PosixFileAttributes#permissions()": [-1, "java.util.Set java.nio.file.FileApiFlips#flipPosixPermissionSet(java.util.Set)"],
+ "void java.nio.file.attribute.PosixFileAttributeView#setPermissions(java.util.Set)": [0, "java.util.Set java.nio.file.FileApiFlips#flipPosixPermissionSet(java.util.Set)"],
+ "java.util.Map java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption[])" : [-1, "java.util.Map java.nio.file.FileApiFlips#flipMapWithMaybeFileTimeValues(java.util.Map)"]
},
"wrapper_conversion": [
"java.nio.channels.AsynchronousChannel",
@@ -117,6 +122,7 @@
"java.nio.file.Path",
"java.nio.file.FileSystem",
"java.nio.file.WatchService",
+ "java.nio.file.WatchEvent",
"java.nio.file.WatchEvent$Kind",
"java.nio.file.WatchKey",
"java.nio.file.Watchable",
@@ -124,19 +130,24 @@
"java.nio.file.WatchEvent$Modifier",
"java.nio.file.attribute.UserPrincipalLookupService",
"java.nio.file.spi.FileSystemProvider",
+ "java.nio.file.spi.FileTypeDetector",
"java.nio.file.AccessMode",
+ "java.nio.file.StandardOpenOption",
"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"
+ "java.nio.file.attribute.PosixFilePermission",
+ "java.nio.file.attribute.BasicFileAttributes",
+ "java.nio.file.attribute.PosixFileAttributes",
+ "java.nio.file.attribute.FileOwnerAttributeView",
+ "java.nio.file.attribute.PosixFileAttributeView",
+ "java.nio.file.attribute.BasicFileAttributeView"
],
"wrapper_conversion_excluding": {
"java.nio.channels.AsynchronousFileChannel": [
@@ -146,7 +157,10 @@
]
},
"custom_conversion": {
- "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions"
+ "java.nio.file.OpenOption": "java.nio.file.OpenOptionConversions",
+ "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions",
+ "java.nio.file.attribute.BasicFileAttributes": "java.nio.file.attribute.FileAttributeConversions",
+ "java.nio.file.attribute.FileAttributeView": "java.nio.file.attribute.FileAttributeConversions"
}
},
{
@@ -219,57 +233,25 @@
"java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
+ "api_generic_conversion": {
+ "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"]
+ },
"wrapper_conversion": [
"java.nio.channels.SeekableByteChannel",
- "java.util.function.IntUnaryOperator",
- "java.util.function.BiFunction",
- "java.util.function.IntConsumer",
- "java.util.function.IntBinaryOperator",
- "java.util.function.UnaryOperator",
- "java.util.function.DoubleConsumer",
- "java.util.function.IntPredicate",
- "java.util.Spliterator$OfLong",
- "java.util.stream.Collector",
- "java.util.function.LongPredicate",
- "java.util.function.ToLongFunction",
- "java.util.function.LongToDoubleFunction",
- "java.util.PrimitiveIterator$OfInt",
- "java.util.function.LongToIntFunction",
- "java.util.function.Predicate",
- "java.util.Spliterator$OfPrimitive",
- "java.util.function.DoubleToIntFunction",
- "java.util.function.ObjDoubleConsumer",
- "java.util.function.BinaryOperator",
- "java.util.stream.DoubleStream",
- "java.util.Spliterator$OfInt",
- "java.util.stream.Stream",
- "java.util.function.ObjLongConsumer",
- "java.util.function.ToDoubleFunction",
- "java.util.stream.IntStream",
- "java.util.function.LongBinaryOperator",
- "java.util.Spliterator$OfDouble",
- "java.util.function.DoubleFunction",
- "java.util.function.ObjIntConsumer",
- "java.util.function.Function",
- "java.util.function.Supplier",
- "java.util.function.DoubleUnaryOperator",
- "java.util.function.BiPredicate",
"java.util.PrimitiveIterator$OfDouble",
- "java.util.function.DoubleBinaryOperator",
+ "java.util.PrimitiveIterator$OfInt",
"java.util.PrimitiveIterator$OfLong",
- "java.util.function.BiConsumer",
- "java.util.function.IntFunction",
- "java.util.stream.LongStream",
- "java.util.function.IntToDoubleFunction",
- "java.util.function.LongFunction",
- "java.util.function.ToIntFunction",
- "java.util.function.LongConsumer",
- "java.util.function.Consumer",
- "java.util.function.IntToLongFunction",
- "java.util.function.DoubleToLongFunction",
- "java.util.function.LongUnaryOperator",
+ "java.util.Spliterator$OfDouble",
+ "java.util.Spliterator$OfInt",
+ "java.util.Spliterator$OfLong",
+ "java.util.Spliterator$OfPrimitive",
"java.util.stream.BaseStream",
- "java.util.function.DoublePredicate"
+ "java.util.stream.Collector",
+ "java.util.stream.Collector$Characteristics",
+ "java.util.stream.DoubleStream",
+ "java.util.stream.IntStream",
+ "java.util.stream.LongStream",
+ "java.util.stream.Stream"
],
"wrapper_conversion_excluding": {
"java.util.Spliterator": [
@@ -395,14 +377,33 @@
"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",
- "java.adapter" : "j$.adapter"
+ "java.adapter" : "j$.adapter",
+ "java.util.ConversionRuntimeException": "j$.util.ConversionRuntimeException"
},
"rewrite_derived_prefix": {
- "java.nio.file.attribute.FileTime": {
- "j$.nio.file.attribute.FileTime": "java.nio.file.attribute.FileTime"
+ "java.nio.file.attribute.": {
+ "j$.nio.file.attribute.": "java.nio.file.attribute."
+ },
+ "java.nio.file.OpenOption": {
+ "j$.nio.file.OpenOption": "java.nio.file.OpenOption"
+ },
+ "java.nio.file.StandardOpenOption": {
+ "j$.nio.file.StandardOpenOption": "java.nio.file.StandardOpenOption"
+ },
+ "java.nio.file.LinkOption": {
+ "j$.nio.file.LinkOption": "java.nio.file.LinkOption"
},
"java.nio.file.Files": {
"j$.nio.file.Files": "java.nio.file.Files"
+ },
+ "java.nio.file.FileSystem": {
+ "j$.nio.file.FileSystem": "java.nio.file.FileSystem"
+ },
+ "java.nio.file.spi.FileSystemProvider": {
+ "j$.nio.file.spi.FileSystemProvider": "java.nio.file.spi.FileSystemProvider"
+ },
+ "java.nio.file.Path": {
+ "j$.nio.file.Path": "java.nio.file.Path"
}
},
"retarget_method": {
@@ -441,6 +442,9 @@
"java.util.LongSummaryStatistics": {
"j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics"
},
+ "java.util.stream.Collector": {
+ "j$.util.stream.Collector": "java.util.stream.Collector"
+ },
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
}
diff --git a/src/main/java/com/android/tools/r8/ApiLevelException.java b/src/main/java/com/android/tools/r8/ApiLevelException.java
deleted file mode 100644
index d58f0ec..0000000
--- a/src/main/java/com/android/tools/r8/ApiLevelException.java
+++ /dev/null
@@ -1,31 +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;
-
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.utils.AndroidApiLevel;
-
-/** Exception to signal features that are not supported until a given API level. */
-public class ApiLevelException extends CompilationError {
-
- public ApiLevelException(
- AndroidApiLevel minApiLevel, String unsupportedFeatures, String sourceString) {
- super(makeMessage(minApiLevel, unsupportedFeatures, sourceString));
- assert minApiLevel != null;
- assert unsupportedFeatures != null;
- }
-
- public static String makeMessage(
- AndroidApiLevel minApiLevel, String unsupportedFeatures, String sourceString) {
- String message =
- unsupportedFeatures
- + " are only supported starting with "
- + minApiLevel.getName()
- + " (--min-api "
- + minApiLevel.getLevel()
- + ")";
- message = (sourceString != null) ? message + ": " + sourceString : message;
- return message;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 203b1d6..7ea383c 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -374,6 +374,11 @@
if (hasDesugaredLibraryConfiguration() && getDisableDesugaring()) {
reporter.error("Using desugared library configuration requires desugaring to be enabled");
}
+ if (getProgramConsumer() instanceof ClassFileConsumer
+ && getDisableDesugaring()
+ && isMinApiLevelSet()) {
+ reporter.error("Compiling to CF with --min-api and --no-desugaring is not supported");
+ }
super.validate();
}
@@ -397,12 +402,18 @@
new InternalGlobalSyntheticsProgramProvider(globalSyntheticsResourceProviders));
}
+ // If compiling to CF with --no-desugaring then the target API is B for consistency with R8.
+ int minApiLevel =
+ getProgramConsumer() instanceof ClassFileConsumer && getDisableDesugaring()
+ ? AndroidApiLevel.B.getLevel()
+ : getMinApiLevel();
+
return new D8Command(
getAppBuilder().build(),
getMode(),
getProgramConsumer(),
getMainDexListConsumer(),
- getMinApiLevel(),
+ minApiLevel,
getReporter(),
getDesugaringState(),
intermediate,
@@ -576,8 +587,7 @@
internal.debug = getMode() == CompilationMode.DEBUG;
internal.programConsumer = getProgramConsumer();
if (internal.isGeneratingClassFiles()) {
- internal.cfToCfDesugar = true;
- // Turn off switch optimizations when desugaring to class file format.
+ // Turn off switch optimizations when generating class files.
assert internal.enableSwitchRewriting;
internal.enableSwitchRewriting = false;
assert internal.enableStringSwitchConversion;
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index dcd8595..4f5b985 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.ParseFlagInfoImpl.flag1;
+
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -34,6 +36,7 @@
"--main-dex-list",
"--main-dex-list-output",
"--desugared-lib",
+ "--desugared-lib-pg-conf-output",
THREAD_COUNT_FLAG);
public static List<ParseFlagInfo> getFlags() {
@@ -64,6 +67,11 @@
"Synthetic classes are with their originating class."))
.add(ParseFlagInfoImpl.flag0("--no-desugaring", "Force disable desugaring."))
.add(ParseFlagInfoImpl.getDesugaredLib())
+ .add(
+ flag1(
+ "--desugared-lib-pg-conf-output",
+ "<file>",
+ "Output the Proguard configuration for L8 to <file>."))
.add(ParseFlagInfoImpl.getMainDexRules())
.add(ParseFlagInfoImpl.getMainDexList())
.add(ParseFlagInfoImpl.getMainDexListOutput())
@@ -291,6 +299,9 @@
builder.setDisableDesugaring(true);
} else if (arg.equals("--desugared-lib")) {
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else if (arg.equals("--desugared-lib-pg-conf-output")) {
+ StringConsumer consumer = new StringConsumer.FileConsumer(Paths.get(nextArg));
+ builder.setDesugaredLibraryKeepRuleConsumer(consumer);
} else if (arg.startsWith("--")) {
if (tryParseAssertionArgument(builder, arg, origin)) {
continue;
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index f8d95ab..1bb6dc5 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -90,13 +90,12 @@
ExecutorService executorService)
throws CompilationFailedException {
try {
- assert !options.cfToCfDesugar;
ExceptionUtils.withD8CompilationHandler(
options.reporter,
() -> {
// Desugar to class file format and turn off switch optimizations, as the final
// compilation with D8 or R8 will do that.
- options.cfToCfDesugar = true;
+ assert options.isCfDesugaring();
assert !options.forceAnnotateSynthetics;
options.forceAnnotateSynthetics = true;
assert options.enableSwitchRewriting;
@@ -106,12 +105,10 @@
desugar(app, options, executorService);
- options.cfToCfDesugar = false;
options.forceAnnotateSynthetics = false;
options.enableSwitchRewriting = true;
options.enableStringSwitchConversion = true;
});
- assert !options.cfToCfDesugar;
if (shrink) {
R8.run(r8Command, executorService);
} else if (d8Command != null) {
@@ -125,7 +122,7 @@
private static void desugar(
AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException {
Timing timing = Timing.create("L8 desugaring", options);
- assert options.cfToCfDesugar;
+ assert options.isCfDesugaring();
try {
// Since L8 Cf representation is temporary, just disable long running back-end optimizations
// on it.
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2fd907e..2145f96 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -72,6 +72,7 @@
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.VisibilityBridgeRemover;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
+import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.repackaging.Repackaging;
@@ -441,6 +442,8 @@
assert appView.appInfo().hasLiveness();
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ assert new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
+
new StartupInstrumentation(appView).instrumentAllClasses(executorService);
assert verifyNoJarApplicationReaders(appView.appInfo().classes());
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 309a475..7923102 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -1013,6 +1013,8 @@
// have a kept subclass, in which case 'm' would leak into the public API.
if (internal.isGeneratingClassFiles()) {
horizontalClassMergerOptions.disable();
+ // R8 CF output does not support desugaring so disable it.
+ internal.desugarState = DesugarState.OFF;
}
// EXPERIMENTAL flags.
diff --git a/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
index a2e9e87..3ff4dcb 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.androidapi;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.structural.Equatable;
import java.util.Objects;
@@ -70,6 +71,10 @@
return this.equals(other);
}
+ OptionalBool isLessThanOrEqualTo(AndroidApiLevel other);
+
+ OptionalBool isLessThanOrEqualTo(ComputedApiLevel other);
+
class NotSetApiLevel implements ComputedApiLevel {
private static final NotSetApiLevel INSTANCE = new NotSetApiLevel();
@@ -77,6 +82,18 @@
private NotSetApiLevel() {}
@Override
+ public OptionalBool isLessThanOrEqualTo(AndroidApiLevel other) {
+ assert false : "Cannot compute relationship for not set";
+ return OptionalBool.unknown();
+ }
+
+ @Override
+ public OptionalBool isLessThanOrEqualTo(ComputedApiLevel other) {
+ assert false : "Cannot compute relationship for not set";
+ return OptionalBool.unknown();
+ }
+
+ @Override
public boolean isNotSetApiLevel() {
return true;
}
@@ -99,6 +116,16 @@
private UnknownApiLevel() {}
@Override
+ public OptionalBool isLessThanOrEqualTo(AndroidApiLevel other) {
+ return OptionalBool.unknown();
+ }
+
+ @Override
+ public OptionalBool isLessThanOrEqualTo(ComputedApiLevel other) {
+ return OptionalBool.unknown();
+ }
+
+ @Override
public boolean isUnknownApiLevel() {
return true;
}
@@ -145,6 +172,20 @@
}
@Override
+ public OptionalBool isLessThanOrEqualTo(AndroidApiLevel other) {
+ return OptionalBool.of(apiLevel.isLessThanOrEqualTo(other));
+ }
+
+ @Override
+ public OptionalBool isLessThanOrEqualTo(ComputedApiLevel other) {
+ if (other.isKnownApiLevel()) {
+ return isLessThanOrEqualTo(other.asKnownApiLevel().getApiLevel());
+ }
+ assert other.isUnknownApiLevel() : "Cannot compute relationship for not set";
+ return OptionalBool.unknown();
+ }
+
+ @Override
public String toString() {
return apiLevel.toString();
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index d2c931e..d1b5e9e 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -536,7 +536,14 @@
} else if (frameType.isUninitializedNew()) {
return frameTypeType() + ".uninitializedNew(new " + cfType("CfLabel") + "())";
} else if (frameType.isPrimitive()) {
- return frameTypeType() + "." + frameType.asPrimitive().getTypeName() + "Type()";
+ if (frameType.isWidePrimitiveHigh()) {
+ return frameTypeType()
+ + "."
+ + frameType.asWidePrimitive().getLowType().getTypeName()
+ + "HighType()";
+ } else {
+ return frameTypeType() + "." + frameType.asPrimitive().getTypeName() + "Type()";
+ }
} else {
assert frameType.isInitialized();
if (frameType.isNullType()) {
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 0c4a184..c3dac32 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -456,14 +456,12 @@
}
private void print(FrameType type) {
- if (type.isPrimitive()) {
- builder.append(type.asPrimitive().getTypeName());
- } else if (type.isInitialized()) {
+ if (type.isInitializedReferenceType()) {
appendType(type.asInitializedReferenceType().getInitializedType());
} else if (type.isUninitializedNew()) {
builder.append("uninitialized ").append(getLabel(type.getUninitializedLabel()));
} else {
- builder.append(type.toString());
+ builder.append(type);
}
}
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 3ed5c3a..2c2c461 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
@@ -205,16 +205,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState state,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState state, AppView<?> appView, CfAnalysisConfig config) {
// ..., value1, value2 →
// ..., result
return state
- .popInitialized(appView, type)
- .popInitialized(appView, type)
+ .popInitialized(appView, config, type)
+ .popInitialized(appView, config, type)
.push(appView, config, type);
}
}
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 deb3f3b..6562ee0 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
@@ -81,13 +81,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., arrayref →
// ..., length
- return frame.popArray(appView).push(config, dexItemFactory.intType);
+ return frame.popArray(appView).push(config, appView.dexItemFactory().intType);
}
}
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 2d719fd..0bae2fd 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.frame.FrameType;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -101,25 +102,27 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., arrayref, index →
// ..., value
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
return frame
- .popInitialized(appView, dexItemFactory.intType)
+ .popInitialized(appView, config, dexItemFactory.intType)
.popInitialized(
appView,
+ config,
getExpectedArrayType(dexItemFactory),
- (state, head) ->
- head.isNullType()
- ? state.push(appView, config, getType())
- : state.push(
- config,
- head.asInitializedReferenceType()
- .getInitializedType()
- .toArrayElementType(dexItemFactory)));
+ (state, head) -> {
+ if (head.isNullType()) {
+ return getType() == MemberType.OBJECT
+ ? state.push(config, FrameType.initialized(DexItemFactory.nullValueType))
+ : state.push(appView, config, getType());
+ }
+ return state.push(
+ config,
+ head.asInitializedReferenceType()
+ .getInitializedType()
+ .toArrayElementType(dexItemFactory));
+ });
}
}
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 9435e85..918203f 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
@@ -92,16 +92,13 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., arrayref, index, value →
// ...
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
return frame
- .popInitialized(appView, getType())
- .popInitialized(appView, dexItemFactory.intType)
- .popInitialized(appView, getExpectedArrayType(dexItemFactory));
+ .popInitialized(appView, config, getType())
+ .popInitialized(appView, config, dexItemFactory.intType)
+ .popInitialized(appView, config, getExpectedArrayType(dexItemFactory));
}
}
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
index 52854e4..5def6cf 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfAssignability.java
@@ -20,19 +20,25 @@
public class CfAssignability {
- public static boolean isFrameTypeAssignable(
- FrameType source, FrameType target, AppView<?> appView) {
+ final AppView<?> appView;
+ final DexItemFactory dexItemFactory;
+
+ public CfAssignability(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ public boolean isFrameTypeAssignable(FrameType source, FrameType target) {
if (source.isSingle() != target.isSingle()) {
return false;
}
return source.isSingle()
- ? isFrameTypeAssignable(source.asSingle(), target.asSingle(), appView)
+ ? isFrameTypeAssignable(source.asSingle(), target.asSingle())
: isFrameTypeAssignable(source.asWide(), target.asWide());
}
// Based on https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.2.
- public static boolean isFrameTypeAssignable(
- SingleFrameType source, SingleFrameType target, AppView<?> appView) {
+ public boolean isFrameTypeAssignable(SingleFrameType source, SingleFrameType target) {
if (source.equals(target) || target.isOneWord()) {
return true;
}
@@ -48,7 +54,6 @@
|| uninitializedNewTypeSource == uninitializedNewTypeTarget;
}
// TODO(b/168190267): Clean-up the lattice.
- DexItemFactory factory = appView.dexItemFactory();
if (target.isPrimitive()) {
return source.isPrimitive()
&& source.asSinglePrimitive().hasIntVerificationType()
@@ -62,25 +67,23 @@
// Both are instantiated types and we resort to primitive type/java type hierarchy checking.
return isAssignable(
source.asInitializedReferenceType().getInitializedType(),
- target.asInitializedReferenceType().getInitializedType(),
- appView);
+ target.asInitializedReferenceType().getInitializedType());
}
- return target.asInitializedReferenceType().getInitializedType() == factory.objectType;
+ return target.asInitializedReferenceType().getInitializedType() == dexItemFactory.objectType;
}
return false;
}
- public static boolean isFrameTypeAssignable(WideFrameType source, WideFrameType target) {
+ public boolean isFrameTypeAssignable(WideFrameType source, WideFrameType target) {
assert !source.isTwoWord();
return source.lessThanOrEqualTo(target);
}
// 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) {
+ public boolean isAssignable(DexType source, DexType target) {
assert !target.isNullValueType();
- DexItemFactory factory = appView.dexItemFactory();
- source = byteCharShortOrBooleanToInt(source, factory);
- target = byteCharShortOrBooleanToInt(target, factory);
+ source = byteCharShortOrBooleanToInt(source, dexItemFactory);
+ target = byteCharShortOrBooleanToInt(target, dexItemFactory);
if (source == target) {
return true;
}
@@ -90,7 +93,7 @@
// Both are now references - everything is assignable to object.
assert source.isReferenceType();
assert target.isReferenceType();
- if (target == factory.objectType) {
+ if (target == dexItemFactory.objectType) {
return true;
}
// isAssignable(null, class(_, _)).
@@ -101,21 +104,24 @@
if (target.isArrayType()) {
return source.isArrayType()
&& isAssignable(
- source.toArrayElementType(factory), target.toArrayElementType(factory), appView);
+ source.toArrayElementType(dexItemFactory), target.toArrayElementType(dexItemFactory));
}
assert target.isClassType();
if (source.isArrayType()) {
// Array types are assignable to the class types Object, Cloneable and Serializable.
// Object is handled above, so we only need to check the other two.
- return target == factory.cloneableType || target == factory.serializableType;
+ return target == dexItemFactory.cloneableType || target == dexItemFactory.serializableType;
}
assert source.isClassType();
- // TODO(b/166570659): Do a sub-type check that allows for missing classes in hierarchy.
+ return internalIsClassTypeAssignableToClassType(source, target);
+ }
+
+ boolean internalIsClassTypeAssignableToClassType(DexType source, DexType target) {
return true;
}
- public static boolean isAssignable(DexType source, ValueType target, AppView<?> appView) {
- return isAssignable(source, target.toDexType(appView.dexItemFactory()), appView);
+ public boolean isAssignable(DexType source, ValueType target) {
+ return isAssignable(source, target.toDexType(dexItemFactory));
}
private static DexType byteCharShortOrBooleanToInt(DexType type, DexItemFactory factory) {
@@ -131,19 +137,13 @@
|| type.isShortType();
}
- public static AssignabilityResult isFrameAssignable(
- CfFrame source, CfFrame target, AppView<?> appView) {
- AssignabilityResult result =
- isLocalsAssignable(source.getLocals(), target.getLocals(), appView);
- return result.isSuccessful()
- ? isStackAssignable(source.getStack(), target.getStack(), appView)
- : result;
+ public AssignabilityResult isFrameAssignable(CfFrame source, CfFrame target) {
+ AssignabilityResult result = isLocalsAssignable(source.getLocals(), target.getLocals());
+ return result.isSuccessful() ? isStackAssignable(source.getStack(), target.getStack()) : result;
}
- public static AssignabilityResult isLocalsAssignable(
- Int2ObjectSortedMap<FrameType> sourceLocals,
- Int2ObjectSortedMap<FrameType> targetLocals,
- AppView<?> appView) {
+ public AssignabilityResult isLocalsAssignable(
+ Int2ObjectSortedMap<FrameType> sourceLocals, Int2ObjectSortedMap<FrameType> targetLocals) {
// 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 = targetLocals.isEmpty() ? -1 : targetLocals.lastIntKey();
@@ -162,7 +162,7 @@
if (sourceType.isWide() && destinationType.isOneWord()) {
destinationType = FrameType.twoWord();
}
- if (!isFrameTypeAssignable(sourceType, destinationType, appView)) {
+ if (!isFrameTypeAssignable(sourceType, destinationType)) {
return new FailedAssignabilityResult(
"Could not assign '"
+ MapUtils.toString(sourceLocals)
@@ -180,10 +180,8 @@
return new SuccessfulAssignabilityResult();
}
- public static AssignabilityResult isStackAssignable(
- Deque<PreciseFrameType> sourceStack,
- Deque<PreciseFrameType> targetStack,
- AppView<?> appView) {
+ public AssignabilityResult isStackAssignable(
+ Deque<PreciseFrameType> sourceStack, Deque<PreciseFrameType> targetStack) {
if (sourceStack.size() != targetStack.size()) {
return new FailedAssignabilityResult(
"Source stack "
@@ -196,7 +194,7 @@
int stackIndex = 0;
for (PreciseFrameType sourceType : sourceStack) {
PreciseFrameType destinationType = otherIterator.next();
- if (!isFrameTypeAssignable(sourceType, destinationType, appView)) {
+ if (!isFrameTypeAssignable(sourceType, destinationType)) {
return new FailedAssignabilityResult(
"Could not assign '"
+ Arrays.toString(sourceStack.toArray())
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 8d7fe37..a45285b 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
@@ -132,13 +132,10 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref →
// ..., objectref
- return frame.popInitialized(appView, dexItemFactory.objectType).push(config, type);
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ return frame.popInitialized(appView, config, dexItemFactory.objectType).push(config, type);
}
}
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 c9edfea..4486ac5 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
@@ -130,16 +130,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value1, value2 →
// ..., result
return frame
- .popInitialized(appView, type)
- .popInitialized(appView, type)
- .push(config, dexItemFactory.intType);
+ .popInitialized(appView, config, type)
+ .popInitialized(appView, config, type)
+ .push(config, appView.dexItemFactory().intType);
}
}
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 b76a1f3..b61202a 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
@@ -139,13 +139,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.classType);
+ return frame.push(config, appView.dexItemFactory().classType);
}
}
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 48a6368..d52f83c 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
@@ -227,13 +227,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.classType);
+ return frame.push(config, appView.dexItemFactory().classType);
}
}
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 0932c43..c58e97a 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
@@ -103,13 +103,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.methodHandleType);
+ return frame.push(config, appView.dexItemFactory().methodHandleType);
}
}
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 74131ae..69a82a9 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
@@ -101,13 +101,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.methodTypeType);
+ return frame.push(config, appView.dexItemFactory().methodTypeType);
}
}
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 ed6f4ea..978b9df 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
@@ -73,11 +73,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
return frame.push(config, DexItemFactory.nullValueType);
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 f777da5..c5841e8 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
@@ -230,11 +230,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
assert type.isPrimitive();
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 a205cb7..a1cf1b8 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
@@ -104,13 +104,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.stringType);
+ return frame.push(config, appView.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 5dcc3fb..7d24f4b 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
@@ -123,13 +123,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., value
- return frame.push(config, dexItemFactory.stringType);
+ return frame.push(config, appView.dexItemFactory().stringType);
}
}
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 8d00469..f989239 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
@@ -12,11 +12,13 @@
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.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
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.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;
@@ -36,8 +38,10 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMaps;
import java.util.ArrayDeque;
import java.util.Deque;
+import java.util.ListIterator;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -98,7 +102,7 @@
// Internal constructor that does not require locals to be of the type Int2ObjectAVLTreeMap.
private CfFrame(Int2ObjectSortedMap<FrameType> locals, Deque<PreciseFrameType> stack) {
- assert locals.values().stream().allMatch(Objects::nonNull);
+ assert CfFrameUtils.verifyLocals(locals);
assert stack.stream().allMatch(Objects::nonNull);
this.locals = locals;
this.stack = stack;
@@ -248,6 +252,24 @@
}
@Override
+ void internalRegisterUse(
+ UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
+ locals.values().forEach(frameType -> internalRegisterUse(registry, frameType));
+ stack.forEach(frameType -> internalRegisterUse(registry, frameType));
+ }
+
+ private void internalRegisterUse(UseRegistry<?> registry, FrameType frameType) {
+ if (frameType.isInitializedReferenceType()) {
+ if (frameType.isNullType()) {
+ return;
+ }
+ registry.registerTypeReference(frameType.asInitializedReferenceType().getInitializedType());
+ } else if (frameType.isUninitializedNew()) {
+ registry.registerTypeReference(frameType.asUninitializedNew().getUninitializedNewType());
+ }
+ }
+
+ @Override
public String toString() {
return getClass().getSimpleName();
}
@@ -274,12 +296,8 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
- return frame.check(appView, this);
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
+ return frame.check(config, this);
}
public static PreciseFrameType getInitializedFrameType(
@@ -295,7 +313,7 @@
return other;
}
- public CfFrame map(java.util.function.Function<DexType, DexType> func) {
+ public CfFrame mapReferenceTypes(Function<DexType, DexType> func) {
boolean mapped = false;
for (int var : locals.keySet()) {
FrameType originalType = locals.get(var);
@@ -319,9 +337,17 @@
}
Builder builder = builder();
for (Int2ObjectMap.Entry<FrameType> entry : locals.int2ObjectEntrySet()) {
- builder.store(entry.getIntKey(), entry.getValue().map(func));
+ FrameType frameType = entry.getValue();
+ if (frameType.isWidePrimitiveHigh()) {
+ // This frame type has already been written as a result of processing the previous frame
+ // type.
+ assert builder.getLocal(entry.getIntKey()) == frameType;
+ continue;
+ }
+ builder.store(entry.getIntKey(), frameType.map(func));
}
for (PreciseFrameType frameType : stack) {
+ assert !frameType.isWidePrimitiveHigh();
builder.push(frameType.map(func));
}
return builder.build();
@@ -395,11 +421,9 @@
}
private Builder internalStore(int localIndex, FrameType frameType) {
- ensureMutableLocals();
- locals.put(localIndex, frameType);
- if (frameType.isWide()) {
- locals.put(localIndex + 1, frameType);
- }
+ assert !frameType.isTwoWord();
+ Int2ObjectAVLTreeMap<FrameType> mutableLocals = ensureMutableLocals();
+ CfFrameUtils.storeLocal(localIndex, frameType, mutableLocals);
return this;
}
@@ -413,10 +437,11 @@
return build();
}
- private void ensureMutableLocals() {
+ private Int2ObjectAVLTreeMap<FrameType> ensureMutableLocals() {
if (locals == EMPTY_LOCALS) {
locals = new Int2ObjectAVLTreeMap<>();
}
+ return (Int2ObjectAVLTreeMap<FrameType>) locals;
}
private void ensureMutableStack() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameUtils.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameUtils.java
new file mode 100644
index 0000000..d632a03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameUtils.java
@@ -0,0 +1,51 @@
+// 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.frame.FrameType;
+import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
+
+public class CfFrameUtils {
+
+ public static void storeLocal(
+ int localIndex, FrameType frameType, Int2ObjectAVLTreeMap<FrameType> locals) {
+ assert !frameType.isTwoWord();
+ // Write low register.
+ FrameType previousType = locals.put(localIndex, frameType);
+ // Set low register -1 to top if it is the start of a wide primitive.
+ if (previousType != null && previousType.isWidePrimitiveHigh()) {
+ FrameType previousLowType = locals.put(localIndex - 1, FrameType.oneWord());
+ assert previousLowType == previousType.asWidePrimitive().getLowType();
+ }
+ // Write high register.
+ if (frameType.isWidePrimitive()) {
+ assert frameType.isWidePrimitiveLow();
+ previousType = locals.put(localIndex + 1, frameType.asWidePrimitive().getHighType());
+ }
+ // Set high register + 1 to top if it is the end of a wide primitive.
+ if (previousType != null && previousType.isWidePrimitiveLow()) {
+ FrameType previousHighType =
+ locals.put(localIndex + frameType.getWidth(), FrameType.oneWord());
+ assert previousHighType == previousType.asWidePrimitive().getHighType();
+ }
+ }
+
+ public static boolean verifyLocals(Int2ObjectSortedMap<FrameType> locals) {
+ for (Int2ObjectMap.Entry<FrameType> entry : locals.int2ObjectEntrySet()) {
+ int localIndex = entry.getIntKey();
+ FrameType frameType = entry.getValue();
+ if (frameType.isWidePrimitiveLow()) {
+ assert locals.get(localIndex + 1) == frameType.asWidePrimitive().getHighType();
+ } else if (frameType.isWidePrimitiveHigh()) {
+ assert locals.get(localIndex - 1) == frameType.asWidePrimitive().getLowType();
+ } else {
+ assert !frameType.isTwoWord();
+ }
+ }
+ return true;
+ }
+}
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 4c21ed6..1a62143 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
@@ -31,6 +31,7 @@
public class CfFrameVerificationHelper implements CfAnalysisConfig {
private final AppView<?> appView;
+ private final CfAssignability assignability;
private final CfCode code;
private final GraphLens codeLens;
private final DexItemFactory factory;
@@ -51,6 +52,7 @@
Map<CfLabel, CfFrame> stateMap,
List<CfTryCatch> tryCatchRanges) {
this.appView = appView;
+ this.assignability = new CfAssignability(appView);
this.code = code;
this.codeLens = codeLens;
this.method = method;
@@ -68,6 +70,11 @@
}
@Override
+ public CfAssignability getAssignability() {
+ return assignability;
+ }
+
+ @Override
public DexMethod getCurrentContext() {
return previousMethod;
}
@@ -132,7 +139,7 @@
}
// From the spec: the handler's exception class is assignable to the class Throwable.
for (DexType guard : tryCatchRange.guards) {
- if (!CfAssignability.isAssignable(guard, factory.throwableType, appView)) {
+ if (!assignability.isAssignable(guard, factory.throwableType)) {
return CfCodeStackMapValidatingException.invalidTryCatchRange(
method,
tryCatchRange,
@@ -141,7 +148,7 @@
}
Deque<PreciseFrameType> sourceStack = ImmutableDeque.of(FrameType.initialized(guard));
AssignabilityResult assignabilityResult =
- CfAssignability.isStackAssignable(sourceStack, destinationFrame.getStack(), appView);
+ assignability.isStackAssignable(sourceStack, destinationFrame.getStack());
if (assignabilityResult.isFailed()) {
return CfCodeStackMapValidatingException.invalidTryCatchRange(
method, tryCatchRange, assignabilityResult.asFailed().getMessage(), appView);
@@ -158,7 +165,7 @@
if (destinationFrame == null) {
return CfFrameState.error("No frame for target catch range target");
}
- state = state.checkLocals(appView, destinationFrame);
+ state = state.checkLocals(this, destinationFrame);
}
}
return state;
@@ -167,7 +174,7 @@
public CfFrameState checkTarget(CfFrameState state, CfLabel label) {
CfFrame destinationFrame = getDestinationFrame(label);
return destinationFrame != null
- ? state.checkLocals(appView, destinationFrame).checkStack(appView, destinationFrame)
+ ? state.checkLocals(this, destinationFrame).checkStack(this, destinationFrame)
: CfFrameState.error("No destination frame");
}
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 e00ee93..ac161ae 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
@@ -108,11 +108,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return frame;
}
}
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 216543f..5858c6f 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
@@ -101,13 +101,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value →
// ...
- return frame.popInitialized(appView, type);
+ return frame.popInitialized(appView, config, type);
}
}
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 2a2e561..63ff9ca 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
@@ -103,13 +103,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value1, value2 →
// ...
- return frame.popInitialized(appView, type).popInitialized(appView, type);
+ return frame.popInitialized(appView, config, type).popInitialized(appView, config, type);
}
}
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 3267d60..164bf07 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
@@ -98,11 +98,8 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
- return frame.readLocal(appView, getLocalIndex(), ValueType.INT, FunctionUtils::getFirst);
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
+ return frame.readLocal(
+ appView, config, getLocalIndex(), ValueType.INT, FunctionUtils::getFirst);
}
}
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 e253413..39735cb 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
@@ -116,13 +116,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., →
// ..., value
- return frame.push(config, dexItemFactory.intType);
+ return frame.push(config, appView.dexItemFactory().intType);
}
}
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 cfd7afa..089f5ee 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
@@ -9,7 +9,6 @@
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.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -67,15 +66,11 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref →
// ..., value
return frame
- .popInitialized(appView, getField().getHolderType())
+ .popInitialized(appView, config, getField().getHolderType())
.push(config, getField().getType());
}
}
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 d18681f..c492db1 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
@@ -11,7 +11,6 @@
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.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -66,17 +65,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref, value →
// ...
return frame
- .popInitialized(appView, getField().getType())
+ .popInitialized(appView, config, getField().getType())
.popObject(
- appView,
getField().getHolderType(),
config,
(state, head) -> head.isUninitializedNew() ? error(head) : state);
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 80da4fe..d13c3a9 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
@@ -126,15 +126,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref →
// ..., result
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
return frame
- .popInitialized(appView, dexItemFactory.objectType)
+ .popInitialized(appView, config, dexItemFactory.objectType)
.push(config, dexItemFactory.intType);
}
}
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 932a283..66e3d69 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
@@ -343,6 +343,11 @@
return false;
}
+ @Override
+ public final boolean instructionTypeCanThrow() {
+ return canThrow();
+ }
+
public abstract void buildIR(IRBuilder builder, CfState state, CfSourceCode code);
/** Return true if this instruction directly emits IR instructions. */
@@ -354,8 +359,5 @@
InliningConstraints inliningConstraints, CfCode code, ProgramMethod context);
public abstract CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory);
+ CfFrameState frame, AppView<?> appView, CfAnalysisConfig config);
}
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 b67dda7..2be9f2e 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
@@ -316,25 +316,22 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref, [arg1, [arg2 ...]] →
// ... [ returnType ]
// OR, for static method calls:
// ..., [arg1, [arg2 ...]] →
// ...
- frame = frame.popInitialized(appView, method.getParameters().getBacking());
+ frame = frame.popInitialized(appView, config, method.getParameters().getBacking());
if (opcode != Opcodes.INVOKESTATIC) {
if (method.getHolderType().isArrayType()) {
frame = frame.popArray(appView);
} else {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
frame =
opcode == Opcodes.INVOKESPECIAL && method.isInstanceInitializer(dexItemFactory)
? frame.popAndInitialize(appView, method, config)
- : frame.popInitialized(appView, method.getHolderType());
+ : frame.popInitialized(appView, config, method.getHolderType());
}
}
if (method.getReturnType().isVoidType()) {
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 a716425..45228d7 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
@@ -167,14 +167,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., [arg1, [arg2 ...]] →
// ...
- frame = frame.popInitialized(appView, callSite.getMethodProto().getParameters().getBacking());
+ frame =
+ frame.popInitialized(
+ appView, config, callSite.getMethodProto().getParameters().getBacking());
DexType returnType = callSite.getMethodProto().getReturnType();
if (returnType.isVoidType()) {
return frame;
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 698e91d..4eaeca8 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
@@ -83,11 +83,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return CfFrameState.error("Unexpected JSR/RET instruction");
}
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 011f97e..4bd6f52 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
@@ -96,11 +96,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return frame;
}
}
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 6831f06..9992cc8 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
@@ -128,15 +128,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., objectref
return frame.readLocal(
appView,
+ config,
getLocalIndex(),
type,
(state, frameType) ->
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 8109ca3..a5f499d 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
@@ -177,11 +177,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value1, value2 →
// ..., result
NumericType value1Type = type;
@@ -196,8 +192,8 @@
value2Type = NumericType.INT;
}
return frame
- .popInitialized(appView, value2Type)
- .popInitialized(appView, value1Type)
+ .popInitialized(appView, config, value2Type)
+ .popInitialized(appView, config, value1Type)
.push(appView, config, value1Type);
}
}
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 f225050..db26e94 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
@@ -94,13 +94,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref →
// ...
- return frame.popInitialized(appView, dexItemFactory.objectType);
+ return frame.popInitialized(appView, config, appView.dexItemFactory().objectType);
}
}
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 4c55cc4..672db34 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
@@ -129,15 +129,12 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., count1, [count2, ...] →
// ..., arrayref
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
for (int i = 0; i < dimensions; i++) {
- frame = frame.popInitialized(appView, dexItemFactory.intType);
+ frame = frame.popInitialized(appView, config, dexItemFactory.intType);
}
return frame.push(config, type);
}
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 bbed094..0cee97d 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
@@ -119,13 +119,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value →
// ..., result
- return frame.popInitialized(appView, type).push(appView, config, type);
+ return frame.popInitialized(appView, config, type).push(appView, config, type);
}
}
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 ec6d665..00fb189 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
@@ -132,11 +132,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., objectref
return frame.push(config, FrameType.uninitializedNew(getLabel(), 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 55a451f..68494c8 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
@@ -165,13 +165,10 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., count →
// ..., arrayref
- return frame.popInitialized(appView, dexItemFactory.intType).push(config, type);
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ return frame.popInitialized(appView, config, dexItemFactory.intType).push(config, type);
}
}
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 febd00e..4b759a2 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
@@ -117,11 +117,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ... →
// ..., objectref
return frame.push(config, type);
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 1e1cc75..b5d7d56 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
@@ -77,11 +77,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return frame;
}
}
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 8b0e54e..40f7298 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
@@ -190,13 +190,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value →
// ..., result
- return frame.popInitialized(appView, from).push(appView, config, to);
+ return frame.popInitialized(appView, config, from).push(appView, config, to);
}
}
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 f6d2d6c..6e7dc78 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
@@ -110,11 +110,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return frame;
}
}
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 18728fd..794872f 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
@@ -110,13 +110,10 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
for (DexField ignored : fields) {
- frame = frame.popInitialized(appView, dexItemFactory.objectType);
+ frame = frame.popInitialized(appView, config, dexItemFactory.objectType);
}
return frame.push(config, dexItemFactory.objectArrayType);
}
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 1bb122a..fe5af15 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
@@ -118,12 +118,10 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
assert !config.getCurrentContext().getReturnType().isVoidType();
- return frame.popInitialized(appView, config.getCurrentContext().getReturnType()).clear();
+ return frame
+ .popInitialized(appView, config, config.getCurrentContext().getReturnType())
+ .clear();
}
}
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 fa1227c..2a25414 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
@@ -92,11 +92,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
return frame.clear();
}
}
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 6971f49..664e746 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
@@ -354,11 +354,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
switch (opcode) {
case Pop:
{
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 c60a5fe..73d6349 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
@@ -9,7 +9,6 @@
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.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -70,11 +69,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., →
// ..., value
return frame.push(config, 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 fa7df86..bdad2a3 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
@@ -8,7 +8,6 @@
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.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -66,13 +65,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., value →
// ...
- return frame.popInitialized(appView, getField().getType());
+ return frame.popInitialized(appView, config, getField().getType());
}
}
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 1015a15..6439925 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
@@ -125,11 +125,7 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., ref →
// ...
if (type.isObject()) {
@@ -138,6 +134,7 @@
assert type.isPrimitive();
return frame.popInitialized(
appView,
+ config,
type,
(state, head) ->
state.storeLocal(getLocalIndex(), type.toPrimitiveType(), appView, config));
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java b/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java
new file mode 100644
index 0000000..0343076
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSubtypingAssignability.java
@@ -0,0 +1,32 @@
+// 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.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+
+public class CfSubtypingAssignability extends CfAssignability {
+
+ public CfSubtypingAssignability(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ }
+
+ @Override
+ boolean internalIsClassTypeAssignableToClassType(DexType source, DexType target) {
+ if (source == target || target == dexItemFactory.objectType) {
+ return true;
+ }
+ if (source.toTypeElement(appView).lessThanOrEqual(target.toTypeElement(appView), appView)) {
+ return true;
+ }
+ DexClass targetClass = appView.definitionFor(target);
+ if (targetClass != null && targetClass.isInterface()) {
+ return true;
+ }
+ return false;
+ }
+}
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 3c3f06c..75ff36d 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
@@ -171,13 +171,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., index/key →
// ...
- return frame.popInitialized(appView, dexItemFactory.intType);
+ return frame.popInitialized(appView, config, appView.dexItemFactory().intType);
}
}
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 c7bc17e..bf50856 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
@@ -99,13 +99,9 @@
}
@Override
- public CfFrameState evaluate(
- CfFrameState frame,
- AppView<?> appView,
- CfAnalysisConfig config,
- DexItemFactory dexItemFactory) {
+ public CfFrameState evaluate(CfFrameState frame, AppView<?> appView, CfAnalysisConfig config) {
// ..., objectref →
// objectref
- return frame.popInitialized(appView, dexItemFactory.throwableType).clear();
+ return frame.popInitialized(appView, config, appView.dexItemFactory().throwableType).clear();
}
}
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 22aac9a..67f452d 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
@@ -12,7 +12,9 @@
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class CfTryCatch {
@@ -29,6 +31,15 @@
assert verifyAllNonNull(guards);
}
+ public void forEach(BiConsumer<DexType, CfLabel> consumer) {
+ Iterator<DexType> guardIterator = guards.iterator();
+ Iterator<CfLabel> targetIterator = targets.iterator();
+ while (guardIterator.hasNext()) {
+ consumer.accept(guardIterator.next(), targetIterator.next());
+ }
+ assert !targetIterator.hasNext();
+ }
+
public void forEachTarget(Consumer<CfLabel> consumer) {
targets.forEach(consumer);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
index 392b16d..e8eb7b3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/BaseFrameType.java
@@ -31,6 +31,16 @@
}
@Override
+ public boolean isDoubleLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isDoubleHigh() {
+ return false;
+ }
+
+ @Override
public boolean isFloat() {
return false;
}
@@ -46,6 +56,16 @@
}
@Override
+ public boolean isLongLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isLongHigh() {
+ return false;
+ }
+
+ @Override
public boolean isShort() {
return false;
}
@@ -99,13 +119,18 @@
}
@Override
+ public boolean isSinglePrimitive() {
+ return false;
+ }
+
+ @Override
public SinglePrimitiveFrameType asSinglePrimitive() {
return null;
}
@Override
public boolean isInitializedReferenceType() {
- return true;
+ return false;
}
@Override
@@ -124,6 +149,26 @@
}
@Override
+ public boolean isWidePrimitive() {
+ return false;
+ }
+
+ @Override
+ public WidePrimitiveFrameType asWidePrimitive() {
+ return null;
+ }
+
+ @Override
+ public boolean isWidePrimitiveLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isWidePrimitiveHigh() {
+ return false;
+ }
+
+ @Override
public int getWidth() {
assert isSingle();
return 1;
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/DoubleFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/DoubleFrameType.java
index 93c0687..41ee1b5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/DoubleFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/DoubleFrameType.java
@@ -14,7 +14,17 @@
static final DoubleFrameType SINGLETON = new DoubleFrameType();
- private DoubleFrameType() {}
+ DoubleFrameType() {}
+
+ @Override
+ public DoubleFrameType getLowType() {
+ return FrameType.doubleType();
+ }
+
+ @Override
+ public DoubleHighFrameType getHighType() {
+ return FrameType.doubleHighType();
+ }
@Override
public boolean isDouble() {
@@ -22,6 +32,26 @@
}
@Override
+ public boolean isDoubleLow() {
+ return true;
+ }
+
+ @Override
+ public boolean isDoubleHigh() {
+ return false;
+ }
+
+ @Override
+ public boolean isWidePrimitiveLow() {
+ return true;
+ }
+
+ @Override
+ public boolean isWidePrimitiveHigh() {
+ return false;
+ }
+
+ @Override
public DexType getInitializedType(DexItemFactory dexItemFactory) {
return dexItemFactory.doubleType;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/DoubleHighFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/DoubleHighFrameType.java
new file mode 100644
index 0000000..7c89492
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/DoubleHighFrameType.java
@@ -0,0 +1,63 @@
+// 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.frame;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DoubleHighFrameType extends DoubleFrameType {
+
+ static final DoubleHighFrameType SINGLETON = new DoubleHighFrameType();
+
+ private DoubleHighFrameType() {}
+
+ @Override
+ public boolean isDouble() {
+ return true;
+ }
+
+ @Override
+ public boolean isDoubleLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isDoubleHigh() {
+ return true;
+ }
+
+ @Override
+ public boolean isWidePrimitiveLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isWidePrimitiveHigh() {
+ return true;
+ }
+
+ @Override
+ public DexType getInitializedType(DexItemFactory dexItemFactory) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String getTypeName() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return "double-high";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
index f614d65..823380f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
@@ -32,6 +32,10 @@
return DoubleFrameType.SINGLETON;
}
+ static DoubleHighFrameType doubleHighType() {
+ return DoubleHighFrameType.SINGLETON;
+ }
+
static FloatFrameType floatType() {
return FloatFrameType.SINGLETON;
}
@@ -44,6 +48,10 @@
return LongFrameType.SINGLETON;
}
+ static LongHighFrameType longHighType() {
+ return LongHighFrameType.SINGLETON;
+ }
+
static ShortFrameType shortType() {
return ShortFrameType.SINGLETON;
}
@@ -52,6 +60,11 @@
if (type.isPrimitiveType()) {
return primitive(type);
}
+ return initializedReference(type);
+ }
+
+ static InitializedReferenceFrameType initializedReference(DexType type) {
+ assert type.isReferenceType();
return new InitializedReferenceFrameType(type);
}
@@ -140,6 +153,10 @@
boolean isDouble();
+ boolean isDoubleLow();
+
+ boolean isDoubleHigh();
+
boolean isFloat();
boolean isInitialized();
@@ -152,6 +169,10 @@
boolean isLong();
+ boolean isLongLow();
+
+ boolean isLongHigh();
+
boolean isNullType();
boolean isObject();
@@ -172,6 +193,8 @@
SingleFrameType asSingle();
+ boolean isSinglePrimitive();
+
SinglePrimitiveFrameType asSinglePrimitive();
boolean isTwoWord();
@@ -192,6 +215,14 @@
WideFrameType asWide();
+ boolean isWidePrimitive();
+
+ WidePrimitiveFrameType asWidePrimitive();
+
+ boolean isWidePrimitiveLow();
+
+ boolean isWidePrimitiveHigh();
+
default FrameType map(Function<DexType, DexType> fn) {
assert !isPrecise();
return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
index fd00888..27ada3e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.cf.code.frame;
-import com.android.tools.r8.errors.Unimplemented;
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.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeUtils;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.Opcodes;
@@ -44,7 +46,8 @@
}
@Override
- public SingleFrameType join(SingleFrameType frameType) {
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
if (equals(frameType)) {
return this;
}
@@ -62,8 +65,10 @@
}
assert type.isArrayType() || type.isClassType();
assert otherType.isArrayType() || otherType.isClassType();
- // TODO(b/214496607): Implement join of different reference types using class hierarchy.
- throw new Unimplemented();
+ DexType joinType =
+ DexTypeUtils.toDexType(
+ appView, type.toTypeElement(appView).join(otherType.toTypeElement(appView), appView));
+ return FrameType.initializedReference(joinType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/LongFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/LongFrameType.java
index 1fb59c7..507c164 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/LongFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/LongFrameType.java
@@ -14,7 +14,17 @@
static final LongFrameType SINGLETON = new LongFrameType();
- private LongFrameType() {}
+ LongFrameType() {}
+
+ @Override
+ public LongFrameType getLowType() {
+ return FrameType.longType();
+ }
+
+ @Override
+ public LongHighFrameType getHighType() {
+ return FrameType.longHighType();
+ }
@Override
public boolean isLong() {
@@ -22,6 +32,26 @@
}
@Override
+ public boolean isLongLow() {
+ return true;
+ }
+
+ @Override
+ public boolean isLongHigh() {
+ return false;
+ }
+
+ @Override
+ public boolean isWidePrimitiveLow() {
+ return true;
+ }
+
+ @Override
+ public boolean isWidePrimitiveHigh() {
+ return false;
+ }
+
+ @Override
public DexType getInitializedType(DexItemFactory dexItemFactory) {
return dexItemFactory.longType;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/LongHighFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/LongHighFrameType.java
new file mode 100644
index 0000000..fadcd79
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/LongHighFrameType.java
@@ -0,0 +1,63 @@
+// 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.frame;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+
+public class LongHighFrameType extends LongFrameType {
+
+ static final LongHighFrameType SINGLETON = new LongHighFrameType();
+
+ private LongHighFrameType() {}
+
+ @Override
+ public boolean isLong() {
+ return true;
+ }
+
+ @Override
+ public boolean isLongLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isLongHigh() {
+ return true;
+ }
+
+ @Override
+ public boolean isWidePrimitiveLow() {
+ return false;
+ }
+
+ @Override
+ public boolean isWidePrimitiveHigh() {
+ return true;
+ }
+
+ @Override
+ public DexType getInitializedType(DexItemFactory dexItemFactory) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String getTypeName() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return "long-high";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java b/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
index 9ac5082..06add68 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.cf.code.frame;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
import org.objectweb.asm.Opcodes;
@@ -25,7 +27,8 @@
}
@Override
- public SingleFrameType join(SingleFrameType frameType) {
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
index aa105e0..dfa0786 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/PreciseFrameType.java
@@ -16,7 +16,7 @@
DexType type = asInitializedReferenceType().getInitializedType();
DexType newType = fn.apply(type);
if (type != newType) {
- return FrameType.initialized(newType);
+ return FrameType.initializedReference(newType);
}
}
if (isUninitializedNew()) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
index 657d493..39c6e70 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.cf.code.frame;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
public interface SingleFrameType extends FrameType {
- SingleFrameType join(SingleFrameType frameType);
+ SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
index 69ba4b6..5525646 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.cf.code.frame;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+
public abstract class SinglePrimitiveFrameType extends SingletonFrameType
implements PrimitiveFrameType, SingleFrameType {
@@ -42,12 +45,18 @@
}
@Override
+ public boolean isSinglePrimitive() {
+ return true;
+ }
+
+ @Override
public final SinglePrimitiveFrameType asSinglePrimitive() {
return this;
}
@Override
- public final SingleFrameType join(SingleFrameType frameType) {
+ public final SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
if (this == frameType) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
index 03eff42..a35e4b4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.cf.code.frame;
import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
@@ -16,6 +18,7 @@
private final DexType type;
public UninitializedNew(CfLabel label, DexType type) {
+ assert type.isClassType();
this.label = label;
this.type = type;
}
@@ -51,7 +54,8 @@
}
@Override
- public SingleFrameType join(SingleFrameType frameType) {
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
return equals(frameType) ? this : FrameType.oneWord();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
index 69d1ecb..5b6730d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.cf.code.frame;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
@@ -36,7 +38,8 @@
}
@Override
- public SingleFrameType join(SingleFrameType frameType) {
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
if (this == frameType) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/WidePrimitiveFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/WidePrimitiveFrameType.java
index 11f7b75..2b46f31 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/WidePrimitiveFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/WidePrimitiveFrameType.java
@@ -7,6 +7,10 @@
public abstract class WidePrimitiveFrameType extends SingletonFrameType
implements PrimitiveFrameType, WideFrameType {
+ public abstract WidePrimitiveFrameType getLowType();
+
+ public abstract WidePrimitiveFrameType getHighType();
+
@Override
public boolean isInitialized() {
return true;
@@ -43,6 +47,16 @@
}
@Override
+ public boolean isWidePrimitive() {
+ return true;
+ }
+
+ @Override
+ public WidePrimitiveFrameType asWidePrimitive() {
+ return this;
+ }
+
+ @Override
public int getWidth() {
return 2;
}
@@ -53,7 +67,7 @@
}
@Override
- public final String toString() {
+ public String toString() {
return getTypeName();
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index aef2eeb..52eafdb 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -316,8 +316,7 @@
}
private void checkName(DexString name) {
- if (!options.itemFactory.getSkipNameValidationForTesting()
- && !name.isValidSimpleName(options.getMinApiLevel())) {
+ if (!options.canUseSpacesInSimpleName() && !name.isValidSimpleName(options.getMinApiLevel())) {
throw new CompilationError(
"Space characters in SimpleName '"
+ name.toASCIIString()
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 2c9ca25..6167f9b 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -47,7 +47,9 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.position.Position;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DexVersion;
@@ -265,10 +267,10 @@
for (DexProgramClass clazz : mapping.getClasses()) {
if (clazz.isInterface()) {
for (DexEncodedMethod method : clazz.directMethods()) {
- checkInterfaceMethod(method);
+ checkInterfaceMethod(clazz, method);
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- checkInterfaceMethod(method);
+ checkInterfaceMethod(clazz, method);
}
}
}
@@ -280,7 +282,7 @@
// -- starting with N interfaces may also have public or private
// static methods, as well as public non-abstract (default)
// and private instance methods.
- private void checkInterfaceMethod(DexEncodedMethod method) {
+ private void checkInterfaceMethod(DexProgramClass holder, DexEncodedMethod method) {
if (appView.dexItemFactory().isClassConstructor(method.getReference())) {
return; // Class constructor is always OK.
}
@@ -288,7 +290,7 @@
if (!options.canUseDefaultAndStaticInterfaceMethods()
&& !options.testing.allowStaticInterfaceMethodsForPreNApiLevel) {
throw options.reporter.fatalError(
- new StaticInterfaceMethodDiagnostic(new MethodPosition(method.getReference())));
+ new StaticInterfaceMethodDiagnostic(holder.getOrigin(), MethodPosition.create(method)));
}
} else {
@@ -299,7 +301,8 @@
if (!method.accessFlags.isAbstract() && !method.accessFlags.isPrivate() &&
!options.canUseDefaultAndStaticInterfaceMethods()) {
throw options.reporter.fatalError(
- new DefaultInterfaceMethodDiagnostic(new MethodPosition(method.getReference())));
+ new DefaultInterfaceMethodDiagnostic(
+ holder.getOrigin(), MethodPosition.create(method)));
}
}
@@ -308,7 +311,7 @@
return;
}
throw options.reporter.fatalError(
- new PrivateInterfaceMethodDiagnostic(new MethodPosition(method.getReference())));
+ new PrivateInterfaceMethodDiagnostic(holder.getOrigin(), MethodPosition.create(method)));
}
if (!method.accessFlags.isPublic()) {
@@ -1378,7 +1381,8 @@
private void checkThatInvokeCustomIsAllowed() {
if (!options.canUseInvokeCustom()) {
- throw options.reporter.fatalError(new InvokeCustomDiagnostic());
+ throw options.reporter.fatalError(
+ new InvokeCustomDiagnostic(Origin.unknown(), Position.UNKNOWN));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/code/CfOrDexInstruction.java b/src/main/java/com/android/tools/r8/dex/code/CfOrDexInstruction.java
index 7bd1e7a..8d7ef16 100644
--- a/src/main/java/com/android/tools/r8/dex/code/CfOrDexInstruction.java
+++ b/src/main/java/com/android/tools/r8/dex/code/CfOrDexInstruction.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.dex.code;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractInstruction;
-public interface CfOrDexInstruction {
+public interface CfOrDexInstruction extends AbstractInstruction {
CfInstruction asCfInstruction();
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
index cc35c46..5efe526 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
@@ -415,4 +415,9 @@
public boolean canThrow() {
return false;
}
+
+ @Override
+ public final boolean instructionTypeCanThrow() {
+ return canThrow();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/errors/ApiLevelDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ApiLevelDiagnostic.java
deleted file mode 100644
index fde6860..0000000
--- a/src/main/java/com/android/tools/r8/errors/ApiLevelDiagnostic.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.errors;
-
-import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.position.Position;
-
-// TODO(b/154778581): Flesh out this class/interface and keep it.
-public abstract class ApiLevelDiagnostic implements Diagnostic {
-
- @Override
- public Origin getOrigin() {
- return Origin.unknown();
- }
-
- @Override
- public Position getPosition() {
- return Position.UNKNOWN;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/errors/ConstMethodHandleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ConstMethodHandleDiagnostic.java
new file mode 100644
index 0000000..d77a5f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/ConstMethodHandleDiagnostic.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.errors;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class ConstMethodHandleDiagnostic extends UnsupportedFeatureDiagnostic {
+
+ public ConstMethodHandleDiagnostic(Origin origin, MethodPosition position) {
+ super("const-method-handle", AndroidApiLevel.P, origin, position);
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.P, "Const-method-handle", getPosition().toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/ConstMethodTypeDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ConstMethodTypeDiagnostic.java
new file mode 100644
index 0000000..2a9eff5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/ConstMethodTypeDiagnostic.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.errors;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class ConstMethodTypeDiagnostic extends UnsupportedFeatureDiagnostic {
+
+ public ConstMethodTypeDiagnostic(Origin origin, MethodPosition position) {
+ super("const-method-type", AndroidApiLevel.P, origin, position);
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.P, "Const-method-type", getPosition().toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/DefaultInterfaceMethodDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DefaultInterfaceMethodDiagnostic.java
index b0dfe4e..21b15b2 100644
--- a/src/main/java/com/android/tools/r8/errors/DefaultInterfaceMethodDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/DefaultInterfaceMethodDiagnostic.java
@@ -3,28 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.errors;
-import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.AndroidApiLevel;
-public class DefaultInterfaceMethodDiagnostic extends ApiLevelDiagnostic {
+public class DefaultInterfaceMethodDiagnostic extends UnsupportedFeatureDiagnostic {
- private final MethodPosition position;
-
- public DefaultInterfaceMethodDiagnostic(MethodPosition position) {
- assert position != null;
- this.position = position;
- }
-
- @Override
- public Position getPosition() {
- return position;
+ public DefaultInterfaceMethodDiagnostic(Origin origin, MethodPosition position) {
+ super("default-interface-method", AndroidApiLevel.N, origin, position);
}
@Override
public String getDiagnosticMessage() {
- return ApiLevelException.makeMessage(
- AndroidApiLevel.N, "Default interface methods", position.toString());
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.N, "Default interface methods", getPosition().toString());
}
}
diff --git a/src/main/java/com/android/tools/r8/errors/InvokeCustomDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InvokeCustomDiagnostic.java
index ab8ff24..0f8c6ab 100644
--- a/src/main/java/com/android/tools/r8/errors/InvokeCustomDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/InvokeCustomDiagnostic.java
@@ -3,13 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.errors;
-import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.AndroidApiLevel;
-public class InvokeCustomDiagnostic extends ApiLevelDiagnostic {
+public class InvokeCustomDiagnostic extends UnsupportedFeatureDiagnostic {
+
+ public InvokeCustomDiagnostic(Origin origin, Position position) {
+ super("invoke-custom", AndroidApiLevel.O, origin, position);
+ }
@Override
public String getDiagnosticMessage() {
- return ApiLevelException.makeMessage(AndroidApiLevel.O, "Invoke-customs", null);
+ return UnsupportedFeatureDiagnostic.makeMessage(AndroidApiLevel.O, "Invoke-customs", null);
}
}
diff --git a/src/main/java/com/android/tools/r8/errors/InvokePolymorphicMethodHandleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InvokePolymorphicMethodHandleDiagnostic.java
new file mode 100644
index 0000000..1efbb31
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InvokePolymorphicMethodHandleDiagnostic.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.errors;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class InvokePolymorphicMethodHandleDiagnostic extends UnsupportedFeatureDiagnostic {
+
+ public InvokePolymorphicMethodHandleDiagnostic(Origin origin, MethodPosition position) {
+ super("invoke-polymorphic-method-handle", AndroidApiLevel.O, origin, position);
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.O,
+ "MethodHandle.invoke and MethodHandle.invokeExact",
+ getPosition().toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/InvokePolymorphicVarHandleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/InvokePolymorphicVarHandleDiagnostic.java
new file mode 100644
index 0000000..9758581
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InvokePolymorphicVarHandleDiagnostic.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.errors;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class InvokePolymorphicVarHandleDiagnostic extends UnsupportedFeatureDiagnostic {
+
+ public InvokePolymorphicVarHandleDiagnostic(Origin origin, MethodPosition position) {
+ super("invoke-polymorphic-var-handle", AndroidApiLevel.P, origin, position);
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.P, "Call to polymorphic signature of VarHandle", getPosition().toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/PrivateInterfaceMethodDiagnostic.java b/src/main/java/com/android/tools/r8/errors/PrivateInterfaceMethodDiagnostic.java
index 568a7a0..9c85254 100644
--- a/src/main/java/com/android/tools/r8/errors/PrivateInterfaceMethodDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/PrivateInterfaceMethodDiagnostic.java
@@ -3,28 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.errors;
-import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.AndroidApiLevel;
-public class PrivateInterfaceMethodDiagnostic extends ApiLevelDiagnostic {
+public class PrivateInterfaceMethodDiagnostic extends UnsupportedFeatureDiagnostic {
- private final MethodPosition position;
-
- public PrivateInterfaceMethodDiagnostic(MethodPosition position) {
- assert position != null;
- this.position = position;
- }
-
- @Override
- public Position getPosition() {
- return position;
+ public PrivateInterfaceMethodDiagnostic(Origin origin, MethodPosition position) {
+ super("private-interface-method", AndroidApiLevel.N, origin, position);
}
@Override
public String getDiagnosticMessage() {
- return ApiLevelException.makeMessage(
- AndroidApiLevel.N, "Private interface methods", position.toString());
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.N, "Private interface methods", getPosition().toString());
}
}
diff --git a/src/main/java/com/android/tools/r8/errors/StaticInterfaceMethodDiagnostic.java b/src/main/java/com/android/tools/r8/errors/StaticInterfaceMethodDiagnostic.java
index cb00b94..9bc4f48 100644
--- a/src/main/java/com/android/tools/r8/errors/StaticInterfaceMethodDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/StaticInterfaceMethodDiagnostic.java
@@ -3,28 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.errors;
-import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.AndroidApiLevel;
-public class StaticInterfaceMethodDiagnostic extends ApiLevelDiagnostic {
+public class StaticInterfaceMethodDiagnostic extends UnsupportedFeatureDiagnostic {
- private final MethodPosition position;
-
- public StaticInterfaceMethodDiagnostic(MethodPosition position) {
- assert position != null;
- this.position = position;
- }
-
- @Override
- public Position getPosition() {
- return position;
+ public StaticInterfaceMethodDiagnostic(Origin origin, MethodPosition position) {
+ super("static-interface-method", AndroidApiLevel.N, origin, position);
}
@Override
public String getDiagnosticMessage() {
- return ApiLevelException.makeMessage(
- AndroidApiLevel.N, "Static interface methods", position.toString());
+ return UnsupportedFeatureDiagnostic.makeMessage(
+ AndroidApiLevel.N, "Static interface methods", getPosition().toString());
}
}
diff --git a/src/main/java/com/android/tools/r8/errors/UnsupportedFeatureDiagnostic.java b/src/main/java/com/android/tools/r8/errors/UnsupportedFeatureDiagnostic.java
new file mode 100644
index 0000000..74c3ad6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/UnsupportedFeatureDiagnostic.java
@@ -0,0 +1,70 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+@Keep
+public abstract class UnsupportedFeatureDiagnostic implements Diagnostic {
+
+ public static String makeMessage(
+ AndroidApiLevel minApiLevel, String unsupportedFeatures, String sourceString) {
+ String message =
+ unsupportedFeatures
+ + " are only supported starting with "
+ + minApiLevel.getName()
+ + " (--min-api "
+ + minApiLevel.getLevel()
+ + ")";
+ message = (sourceString != null) ? message + ": " + sourceString : message;
+ return message;
+ }
+
+ private final String descriptor;
+ private final AndroidApiLevel supportedApiLevel;
+ private final Origin origin;
+ private final Position position;
+
+ // Package-private constructor.
+ UnsupportedFeatureDiagnostic(
+ String descriptor, AndroidApiLevel supportedApiLevel, Origin origin, Position position) {
+ this.descriptor = descriptor;
+ this.supportedApiLevel = supportedApiLevel;
+ this.origin = origin;
+ this.position = position;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ /**
+ * Get a descriptor for the unsupported feature.
+ *
+ * <p>This descriptor is guaranteed by the compiler to be unique for possible unsupported features
+ * and will remain unchanged in future versions of the compiler.
+ */
+ public String getFeatureDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Get the API level at which this feature is supported.
+ *
+ * @return Supported level or -1 if unsupported at all API levels known to the compiler.
+ */
+ public int getSupportedApiLevel() {
+ return supportedApiLevel == null ? -1 : supportedApiLevel.getLevel();
+ }
+}
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 f0598cd..44289af 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -281,6 +281,10 @@
return tryCatchRanges;
}
+ public CfInstruction getInstruction(int index) {
+ return instructions.get(index);
+ }
+
public List<CfInstruction> getInstructions() {
return Collections.unmodifiableList(instructions);
}
@@ -380,7 +384,7 @@
// In cf to cf desugar we do pass through of code and don't move around methods.
// TODO(b/169115389): Remove when we have a way to determine if we need parameter names per
// method.
- if (appView.options().cfToCfDesugar) {
+ if (appView.options().isCfDesugaring()) {
return false;
}
@@ -978,7 +982,7 @@
if (instruction.isLabel()) {
helper.seenLabel(instruction.asLabel());
}
- state = instruction.evaluate(state, appView, helper, appView.dexItemFactory());
+ state = instruction.evaluate(state, appView, helper);
if (instruction.isJumpWithNormalTarget()) {
CfInstruction fallthroughInstruction =
(i + 1) < instructions.size() ? instructions.get(i + 1) : null;
@@ -1046,7 +1050,7 @@
DexMethod previousMethodSignature,
boolean previousMethodSignatureIsInstance) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
- CfFrameState state = ConcreteCfFrameState.bottom();
+ CfFrameState state = new ConcreteCfFrameState();
int localIndex = 0;
if (previousMethodSignatureIsInstance) {
state =
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
index 12baacf..17ba862 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -14,10 +14,12 @@
DexClass toSingleClassWithProgramOverLibrary();
+ DexClass toSingleClassWithLibraryOverProgram();
+
// The alternative class is:
// - the other class than the single class if the resolution resolves into multiple classes,
// - null if the resolution resolves into a single class.
- DexClass toAlternativeClassWithProgramOverLibrary();
+ DexClass toAlternativeClass();
void forEachClassResolutionResult(Consumer<DexClass> consumer);
@@ -95,7 +97,12 @@
}
@Override
- public DexClass toAlternativeClassWithProgramOverLibrary() {
+ public DexClass toSingleClassWithLibraryOverProgram() {
+ return null;
+ }
+
+ @Override
+ public DexClass toAlternativeClass() {
return null;
}
@@ -147,7 +154,12 @@
}
@Override
- public DexClass toAlternativeClassWithProgramOverLibrary() {
+ public DexClass toSingleClassWithLibraryOverProgram() {
+ return libraryClass;
+ }
+
+ @Override
+ public DexClass toAlternativeClass() {
return libraryClass;
}
}
@@ -166,7 +178,12 @@
}
@Override
- public DexClass toAlternativeClassWithProgramOverLibrary() {
+ public DexClass toSingleClassWithLibraryOverProgram() {
+ return libraryClass;
+ }
+
+ @Override
+ public DexClass toAlternativeClass() {
return programOrClasspathClass;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f10a654..265fc65 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -156,7 +156,12 @@
}
@Override
- public DexClass toAlternativeClassWithProgramOverLibrary() {
+ public DexClass toSingleClassWithLibraryOverProgram() {
+ return this;
+ }
+
+ @Override
+ public DexClass toAlternativeClass() {
return null;
}
@@ -287,6 +292,10 @@
return Iterables.filter(virtualMethods(), predicate::test);
}
+ public void addVirtualMethod(DexEncodedMethod method) {
+ methodCollection.addVirtualMethod(method);
+ }
+
public void addVirtualMethods(Collection<DexEncodedMethod> methods) {
methodCollection.addVirtualMethods(methods);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 73e3420..51a4a4a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1100,8 +1100,14 @@
.build());
}
+ public DexEncodedMethod toForwardingMethod(DexClass newHolder, AppView<?> definitions) {
+ return toForwardingMethod(newHolder, definitions, ConsumerUtils.emptyConsumer());
+ }
+
public DexEncodedMethod toForwardingMethod(
- DexClass newHolder, DexDefinitionSupplier definitions) {
+ DexClass newHolder,
+ AppView<?> definitions,
+ Consumer<DexEncodedMethod.Builder> builderConsumer) {
DexMethod newMethod = getReference().withHolder(newHolder, definitions.dexItemFactory());
checkIfObsolete();
@@ -1147,6 +1153,7 @@
.modifyAccessFlags(MethodAccessFlags::setBridge))
.setIsLibraryMethodOverrideIf(
!isStatic() && !isLibraryMethodOverride().isUnknown(), isLibraryMethodOverride())
+ .apply(builderConsumer)
.build();
}
@@ -1471,6 +1478,16 @@
}
if (argumentInfoCollection.hasArgumentPermutation()) {
+ // If we have missing parameter annotations we cannot reliably reorder without handling
+ // missing annotations. We could introduce empty annotations to fill in empty spots but the
+ // missing parameters are only bridged in the reflection api for enums or local/anonymous
+ // classes and permuting such method arguments destroys the "invariant" that these are
+ // shifted.
+ // Having a keep on the members will automatically remove the permutation so the developer
+ // can easily recover.
+ if (newNumberOfMissingParameterAnnotations > 0) {
+ return setParameterAnnotations(ParameterAnnotationsList.empty());
+ }
List<DexAnnotationSet> newPermutedParameterAnnotations =
Arrays.asList(new DexAnnotationSet[method.getParameters().size()]);
for (int parameterIndex = newNumberOfMissingParameterAnnotations;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e9f0446..5c8046f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2045,6 +2045,7 @@
public final DexMethod appendObject;
public final DexMethod appendString;
public final DexMethod appendStringBuffer;
+ public final DexMethod capacity;
public final DexMethod charSequenceConstructor;
public final DexMethod defaultConstructor;
public final DexMethod intConstructor;
@@ -2056,7 +2057,6 @@
private StringBuildingMethods(DexType receiver) {
DexString append = createString("append");
-
appendBoolean = createMethod(receiver, createProto(receiver, booleanType), append);
appendChar = createMethod(receiver, createProto(receiver, charType), append);
appendCharArray = createMethod(receiver, createProto(receiver, charArrayType), append);
@@ -2072,6 +2072,7 @@
appendObject = createMethod(receiver, createProto(receiver, objectType), append);
appendString = createMethod(receiver, createProto(receiver, stringType), append);
appendStringBuffer = createMethod(receiver, createProto(receiver, stringBufferType), append);
+ capacity = createMethod(receiver, createProto(intType), createString("capacity"));
charSequenceConstructor =
createMethod(receiver, createProto(voidType, charSequenceType), constructorMethodName);
defaultConstructor = createMethod(receiver, createProto(voidType), constructorMethodName);
@@ -2114,6 +2115,14 @@
return method == appendObject;
}
+ public boolean isAppendCharSequenceMethod(DexMethod method) {
+ return method == appendCharSequence;
+ }
+
+ public boolean isAppendObjectOrCharSequenceMethod(DexMethod method) {
+ return isAppendObjectMethod(method) || isAppendCharSequenceMethod(method);
+ }
+
public boolean isAppendPrimitiveMethod(DexMethod method) {
return appendPrimitiveMethods.contains(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index b49abbe..5e6c879 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -694,10 +694,6 @@
methodCollection.addMethod(method);
}
- public void addVirtualMethod(DexEncodedMethod virtualMethod) {
- methodCollection.addVirtualMethod(virtualMethod);
- }
-
public void replaceVirtualMethod(
DexMethod virtualMethod, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
methodCollection.replaceVirtualMethod(virtualMethod, replacement);
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
index bff5528..62531ba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -18,7 +18,7 @@
return toDexType(appView, join);
}
- private static DexType toDexType(
+ public static DexType toDexType(
AppView<? extends AppInfoWithClassHierarchy> appView, TypeElement type) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (type.isPrimitiveType()) {
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 0237c6d..f669406 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -562,7 +562,7 @@
}
private void checkName(String name) {
- if (!application.getFactory().getSkipNameValidationForTesting()
+ if (!application.options.canUseSpacesInSimpleName()
&& !DexString.isValidSimpleName(application.options.getMinApiLevel(), name)) {
throw new CompilationError("Space characters in SimpleName '"
+ name + "' are not allowed prior to DEX version 040");
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 8730c35..5b43e8d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -344,6 +344,15 @@
return null;
}
+ private static DexClass definitionForHelper(AppInfoWithClassHierarchy appInfo, DexType type) {
+ if (type == null) {
+ return null;
+ }
+ return appInfo
+ .contextIndependentDefinitionForWithResolutionResult(type)
+ .toSingleClassWithLibraryOverProgram();
+ }
+
/**
* Lookup the target of an invoke-super.
*
@@ -447,7 +456,7 @@
&& !symbolicReference.isInterface()
&& isSuperclass.test(symbolicReference, context)) {
// If reference is a super type of the context then search starts at the immediate super.
- initialType = context.superType == null ? null : appInfo.definitionFor(context.superType);
+ initialType = definitionForHelper(appInfo, context.superType);
} else {
// Otherwise it starts at the reference itself.
initialType = symbolicReference;
@@ -465,7 +474,7 @@
if (target != null) {
break;
}
- current = current.superType == null ? null : appInfo.definitionFor(current.superType);
+ current = definitionForHelper(appInfo, current.superType);
}
// 4. Otherwise, it is the single maximally specific method:
if (target == null) {
@@ -698,7 +707,7 @@
Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
if (lambdaInstance.getMainMethod().match(resolvedMethod)) {
DexMethod methodReference = lambdaInstance.implHandle.asMethod();
- DexClass holder = appInfo.definitionForHolder(methodReference);
+ DexClass holder = definitionForHelper(appInfo, methodReference.getHolderType());
DexClassAndMethod method = methodReference.lookupMemberOnClass(holder);
if (method == null) {
// The targeted method might not exist, eg, Throwable.addSuppressed in an old library.
@@ -743,7 +752,7 @@
if (current.type == overrideTarget.getHolderType()) {
return null;
}
- current = current.superType == null ? null : appInfo.definitionFor(current.superType);
+ current = definitionForHelper(appInfo, current.superType);
continue;
}
DexClassAndMethod target = DexClassAndMethod.create(current, candidate);
@@ -817,11 +826,11 @@
}
private static DexClassAndMethod findWideningOverride(
- DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appView) {
+ DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appInfo) {
// Otherwise, lookup to first override that is distinct from resolvedMethod.
assert resolvedMethod.getDefinition().accessFlags.isPackagePrivate();
while (clazz.superType != null) {
- clazz = appView.definitionFor(clazz.superType);
+ clazz = definitionForHelper(appInfo, clazz.superType);
if (clazz == null) {
return resolvedMethod;
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index aebbc12..30a6dfa 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.LookupTarget;
@@ -70,6 +71,11 @@
}
@Override
+ public void processNewLiveNonProgramType(ClasspathOrLibraryClass clazz) {
+ clazz.forEachClassMethod(this::computeAndSetApiLevelForDefinition);
+ }
+
+ @Override
public void notifyMarkVirtualDispatchTargetAsLive(
LookupTarget target, EnqueuerWorklist worklist) {
target.accept(
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 8dc26d6..d587b61 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph.analysis;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupTarget;
@@ -35,6 +36,9 @@
Enqueuer enqueuer,
EnqueuerWorklist worklist) {}
+ /** Called when a non program class is visited and marked live */
+ public void processNewLiveNonProgramType(ClasspathOrLibraryClass clazz) {}
+
/** Called when a method's code has been processed by the registry. */
public void processTracedCode(
ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractInstruction.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractInstruction.java
new file mode 100644
index 0000000..d68129f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractInstruction.java
@@ -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.ir.analysis.framework.intraprocedural;
+
+public interface AbstractInstruction {
+
+ boolean instructionTypeCanThrow();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
index 914cc9e..f940edf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.BasicBlock;
/** The abstract state of the dataflow analysis, which is computed for each {@link BasicBlock}. */
@@ -16,10 +17,10 @@
return asAbstractState();
}
- public abstract StateType join(StateType state);
+ public abstract StateType join(AppView<?> appView, StateType state);
- public boolean isGreaterThanOrEquals(StateType state) {
- StateType leastUpperBound = join(state);
+ public boolean isGreaterThanOrEquals(AppView<?> appView, StateType state) {
+ StateType leastUpperBound = join(appView, state);
return equals(leastUpperBound);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
index 4e73e2a..ad58a5c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.DexType;
+
/**
* A transfer function that defines the abstract semantics of the instructions in the program
* according to some abstract state {@link StateType}.
@@ -11,14 +13,48 @@
public interface AbstractTransferFunction<
Block, Instruction, StateType extends AbstractState<StateType>> {
+ /** Applies the effect of the given instruction on the given abstract state. */
TransferFunctionResult<StateType> apply(Instruction instruction, StateType state);
+ default TransferFunctionResult<StateType> applyBlock(Block block, StateType state) {
+ return state;
+ }
+
+ /**
+ * Computes the analysis state for the method entry point, i.e., the state prior to the first
+ * instruction.
+ */
default StateType computeInitialState(Block entryBlock, StateType bottom) {
return bottom;
}
+ /** Transfers the state from predecessor block to its successor block. */
default StateType computeBlockEntryState(
Block block, Block predecessor, StateType predecessorExitState) {
return predecessorExitState;
}
+
+ /**
+ * Returns true if (a function of) the abstract state at the given (throwing) instruction should
+ * be transferred to the active catch handlers.
+ */
+ default boolean shouldTransferExceptionalControlFlowFromInstruction(
+ Block throwBlock, Instruction throwInstruction) {
+ return true;
+ }
+
+ /**
+ * Transfers the state from the given (throwing) instruction to its catch handler.
+ *
+ * <p>Only called if {@link #shouldTransferExceptionalControlFlowFromInstruction} has returned
+ * true.
+ */
+ default StateType computeExceptionalBlockEntryState(
+ Block block,
+ DexType guard,
+ Block throwBlock,
+ Instruction throwInstruction,
+ StateType throwState) {
+ return throwState;
+ }
}
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 db0d7ca..51e185c 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
@@ -4,20 +4,51 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.TraversalUtils;
+import com.android.tools.r8.utils.TriFunction;
+import java.util.Collection;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
public interface ControlFlowGraph<Block, Instruction> {
+ Collection<? extends Block> getBlocks();
+
Block getEntryBlock();
+ default Block getUniquePredecessor(Block block) {
+ assert hasUniquePredecessor(block);
+ return TraversalUtils.getFirst(collector -> traversePredecessors(block, collector));
+ }
+
+ default Block getUniqueSuccessor(Block block) {
+ assert hasUniqueSuccessor(block);
+ return TraversalUtils.getFirst(collector -> traverseSuccessors(block, collector));
+ }
+
+ default boolean hasExceptionalPredecessors(Block block) {
+ return TraversalUtils.hasNext(counter -> traverseExceptionalPredecessors(block, counter));
+ }
+
+ default boolean hasExceptionalSuccessors(Block block) {
+ return TraversalUtils.hasNext(
+ counter ->
+ traverseExceptionalSuccessors(
+ block, (exceptionalSuccessor, guard) -> counter.apply(exceptionalSuccessor)));
+ }
+
default boolean hasUniquePredecessor(Block block) {
return TraversalUtils.isSingleton(counter -> traversePredecessors(block, counter));
}
+ default boolean hasUniquePredecessorWithUniqueSuccessor(Block block) {
+ return hasUniquePredecessor(block) && hasUniqueSuccessor(getUniquePredecessor(block));
+ }
+
default boolean hasUniqueSuccessor(Block block) {
return TraversalUtils.isSingleton(counter -> traverseSuccessors(block, counter));
}
@@ -26,11 +57,6 @@
return hasUniqueSuccessor(block) && hasUniquePredecessor(getUniqueSuccessor(block));
}
- default Block getUniqueSuccessor(Block block) {
- assert hasUniqueSuccessor(block);
- return TraversalUtils.getFirst(collector -> traverseSuccessors(block, collector));
- }
-
// Block traversal.
default <BT, CT> TraversalContinuation<BT, CT> traversePredecessors(
@@ -60,8 +86,9 @@
}
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 block, BiFunction<? super Block, DexType, TraversalContinuation<BT, CT>> fn) {
+ return traverseExceptionalSuccessors(
+ block, (successor, guard, ignore) -> fn.apply(successor, guard), null);
}
// Block traversal with result.
@@ -93,7 +120,10 @@
return traverseNormalSuccessors(block, fn, initialValue)
.ifContinueThen(
continuation ->
- traverseExceptionalSuccessors(block, fn, continuation.getValueOrDefault(null)));
+ traverseExceptionalSuccessors(
+ block,
+ (exceptionalSuccessor, guard, value) -> fn.apply(exceptionalSuccessor, value),
+ continuation.getValueOrDefault(null)));
}
<BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors(
@@ -103,7 +133,7 @@
<BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors(
Block block,
- BiFunction<? super Block, ? super CT, TraversalContinuation<BT, CT>> fn,
+ TriFunction<? super Block, DexType, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue);
// Block iteration.
@@ -133,7 +163,8 @@
default void forEachSuccessor(Block block, Consumer<Block> consumer) {
forEachNormalSuccessor(block, consumer);
- forEachExceptionalSuccessor(block, consumer);
+ forEachExceptionalSuccessor(
+ block, (exceptionalSuccessor, guard) -> consumer.accept(exceptionalSuccessor));
}
default void forEachNormalSuccessor(Block block, Consumer<Block> consumer) {
@@ -145,11 +176,11 @@
});
}
- default void forEachExceptionalSuccessor(Block block, Consumer<Block> consumer) {
+ default void forEachExceptionalSuccessor(Block block, BiConsumer<Block, DexType> consumer) {
traverseExceptionalSuccessors(
block,
- exceptionalSuccessor -> {
- consumer.accept(exceptionalSuccessor);
+ (exceptionalSuccessor, guard) -> {
+ consumer.accept(exceptionalSuccessor, guard);
return TraversalContinuation.doContinue();
});
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 8974bbf..73eeb55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.BasicBlock;
import java.util.Map;
@@ -43,10 +44,10 @@
this.blockExitStates = blockExitStates;
}
- public StateType join() {
+ public StateType join(AppView<?> appView) {
StateType result = null;
for (StateType blockExitState : blockExitStates.values()) {
- result = result != null ? result.join(blockExitState) : blockExitState;
+ result = result != null ? result.join(appView, blockExitState) : blockExitState;
}
return result;
}
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 2cc7448..3a8b75c 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.FailedDataflowAnalysisResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
import com.android.tools.r8.utils.Timing;
@@ -25,7 +26,9 @@
* FailedDataflowAnalysisResult}.
*/
public class IntraProceduralDataflowAnalysisBase<
- Block, Instruction, StateType extends AbstractState<StateType>> {
+ Block, Instruction extends AbstractInstruction, StateType extends AbstractState<StateType>> {
+
+ final AppView<?> appView;
final StateType bottom;
@@ -34,21 +37,30 @@
// The transfer function that defines the abstract semantics for each instruction.
final AbstractTransferFunction<Block, Instruction, StateType> transfer;
- // The state of the analysis.
- final Map<Block, StateType> blockExitStates = new IdentityHashMap<>();
-
// The entry states for each block that satisfies the predicate
// shouldCacheBlockEntryStateFor(block). These entry states can be computed from the exit states
// of the predecessors, but doing so can be expensive when a block has many predecessors.
- final Map<Block, StateType> blockEntryStatesCache = new IdentityHashMap<>();
+ final Map<Block, StateType> blockEntryStates = new IdentityHashMap<>();
+
+ // The state of the analysis.
+ final Map<Block, StateType> blockExitStates = new IdentityHashMap<>();
+
+ // The entry states for exceptional blocks.
+ final Map<Block, StateType> exceptionalBlockEntryStates = new IdentityHashMap<>();
+
+ final IntraProceduralDataflowAnalysisOptions options;
public IntraProceduralDataflowAnalysisBase(
+ AppView<?> appView,
StateType bottom,
ControlFlowGraph<Block, Instruction> cfg,
- AbstractTransferFunction<Block, Instruction, StateType> transfer) {
+ AbstractTransferFunction<Block, Instruction, StateType> transfer,
+ IntraProceduralDataflowAnalysisOptions options) {
+ this.appView = appView;
this.bottom = bottom;
this.cfg = cfg;
this.transfer = transfer;
+ this.options = options;
}
public DataflowAnalysisResult run(Block root) {
@@ -69,12 +81,27 @@
StateType state =
timing.time("Compute block entry state", () -> computeBlockEntryState(initialBlock));
+ TransferFunctionResult<StateType> blockResult = transfer.applyBlock(initialBlock, state);
+ if (blockResult.isFailedTransferResult()) {
+ return new FailedDataflowAnalysisResult();
+ }
+ state = blockResult.asAbstractState();
+
timing.begin("Compute transfers");
do {
+ Block currentBlock = block;
+ boolean hasExceptionalSuccessors = cfg.hasExceptionalSuccessors(block);
TraversalContinuation<FailedDataflowAnalysisResult, StateType> traversalContinuation =
cfg.traverseInstructions(
block,
(instruction, previousState) -> {
+ if (instruction.instructionTypeCanThrow()
+ && hasExceptionalSuccessors
+ && transfer.shouldTransferExceptionalControlFlowFromInstruction(
+ currentBlock, instruction)) {
+ updateBlockEntryStateCacheForExceptionalSuccessors(
+ currentBlock, instruction, previousState);
+ }
TransferFunctionResult<StateType> transferResult =
transfer.apply(instruction, previousState);
if (transferResult.isFailedTransferResult()) {
@@ -89,7 +116,7 @@
return traversalContinuation.asBreak().getValue();
}
state = traversalContinuation.asContinue().getValue();
- if (cfg.hasUniqueSuccessorWithUniquePredecessor(block)) {
+ if (isBlockWithIntermediateSuccessorBlock(block)) {
block = cfg.getUniqueSuccessor(block);
} else {
end = block;
@@ -104,53 +131,97 @@
cfg.forEachSuccessor(end, worklist::addIgnoringSeenSet);
}
- // Add the computed exit state to the entry state of each successor that satisfies the
+ // Add the computed exit state to the entry state of each normal successor that satisfies the
// predicate shouldCacheBlockEntryStateFor(successor).
- updateBlockEntryStateCacheForSuccessors(end, state);
+ updateBlockEntryStateCacheForNormalSuccessors(end, state);
}
return new SuccessfulDataflowAnalysisResult<>(blockExitStates);
}
public StateType computeBlockEntryState(Block block) {
if (block == cfg.getEntryBlock()) {
- return transfer.computeInitialState(block, bottom);
+ return transfer
+ .computeInitialState(block, bottom)
+ .join(appView, computeBlockEntryStateForNormalBlock(block));
}
- if (shouldCacheBlockEntryStateFor(block)) {
- return blockEntryStatesCache.getOrDefault(block, bottom);
+ if (cfg.hasExceptionalPredecessors(block)) {
+ return exceptionalBlockEntryStates.getOrDefault(block, bottom).clone();
}
+ return computeBlockEntryStateForNormalBlock(block);
+ }
+
+ private StateType computeBlockEntryStateForNormalBlock(Block block) {
+ if (shouldCacheBlockEntryStateForNormalBlock(block)) {
+ return blockEntryStates.getOrDefault(block, bottom).clone();
+ }
+ return computeBlockEntryStateFromPredecessorExitStates(block);
+ }
+
+ private StateType computeBlockEntryStateFromPredecessorExitStates(Block block) {
TraversalContinuation<?, StateType> traversalContinuation =
- cfg.traversePredecessors(
+ cfg.traverseNormalPredecessors(
block,
(predecessor, entryState) -> {
StateType edgeState =
transfer.computeBlockEntryState(
- block, predecessor, blockExitStates.getOrDefault(predecessor, bottom));
- return TraversalContinuation.doContinue(entryState.join(edgeState));
+ block,
+ predecessor,
+ blockExitStates.getOrDefault(predecessor, bottom).clone());
+ return TraversalContinuation.doContinue(entryState.join(appView, edgeState));
},
bottom);
return traversalContinuation.asContinue().getValue().clone();
}
boolean setBlockExitState(Block block, StateType state) {
- assert !cfg.hasUniqueSuccessorWithUniquePredecessor(block);
+ assert !isBlockWithIntermediateSuccessorBlock(block);
StateType previous = blockExitStates.put(block, state);
- assert previous == null || state.isGreaterThanOrEquals(previous);
+ assert previous == null || state.isGreaterThanOrEquals(appView, previous);
return !state.equals(previous);
}
- void updateBlockEntryStateCacheForSuccessors(Block block, StateType state) {
- cfg.forEachSuccessor(
+ void updateBlockEntryStateCacheForNormalSuccessors(Block block, StateType state) {
+ cfg.forEachNormalSuccessor(
block,
successor -> {
- if (shouldCacheBlockEntryStateFor(successor)) {
+ if (shouldCacheBlockEntryStateForNormalBlock(successor)) {
StateType edgeState = transfer.computeBlockEntryState(successor, block, state);
- StateType previous = blockEntryStatesCache.getOrDefault(successor, bottom);
- blockEntryStatesCache.put(successor, previous.join(edgeState));
+ updateBlockEntryStateForBlock(successor, edgeState, blockEntryStates);
}
});
}
- boolean shouldCacheBlockEntryStateFor(Block block) {
+ void updateBlockEntryStateCacheForExceptionalSuccessors(
+ Block block, Instruction instruction, StateType state) {
+ cfg.forEachExceptionalSuccessor(
+ block,
+ (exceptionalSuccessor, guard) -> {
+ StateType edgeState =
+ transfer.computeExceptionalBlockEntryState(
+ exceptionalSuccessor, guard, block, instruction, state);
+ updateBlockEntryStateForBlock(
+ exceptionalSuccessor, edgeState, exceptionalBlockEntryStates);
+ });
+ }
+
+ private void updateBlockEntryStateForBlock(
+ Block block, StateType edgeState, Map<Block, StateType> states) {
+ StateType previous = states.getOrDefault(block, bottom);
+ states.put(block, previous.join(appView, edgeState));
+ }
+
+ public boolean isIntermediateBlock(Block block) {
+ return options.isCollapsingOfTrivialEdgesEnabled()
+ && cfg.hasUniquePredecessorWithUniqueSuccessor(block)
+ && block != cfg.getEntryBlock()
+ && !cfg.hasExceptionalPredecessors(block);
+ }
+
+ public boolean isBlockWithIntermediateSuccessorBlock(Block block) {
+ return cfg.hasUniqueSuccessor(block) && isIntermediateBlock(cfg.getUniqueSuccessor(block));
+ }
+
+ boolean shouldCacheBlockEntryStateForNormalBlock(Block block) {
return TraversalUtils.isSizeGreaterThan(counter -> cfg.traversePredecessors(block, counter), 2);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisOptions.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisOptions.java
new file mode 100644
index 0000000..c079bbf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisOptions.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;
+
+public class IntraProceduralDataflowAnalysisOptions {
+
+ private static final IntraProceduralDataflowAnalysisOptions COLLAPSE_INSTANCE =
+ new IntraProceduralDataflowAnalysisOptions(true);
+ private static final IntraProceduralDataflowAnalysisOptions NO_COLLAPSE_INSTANCE =
+ new IntraProceduralDataflowAnalysisOptions(false);
+
+ private final boolean isCollapsingOfTrivialEdgesEnabled;
+
+ IntraProceduralDataflowAnalysisOptions(boolean isCollapsingOfTrivialEdgesEnabled) {
+ this.isCollapsingOfTrivialEdgesEnabled = isCollapsingOfTrivialEdgesEnabled;
+ }
+
+ public boolean isCollapsingOfTrivialEdgesEnabled() {
+ return isCollapsingOfTrivialEdgesEnabled;
+ }
+
+ public static IntraProceduralDataflowAnalysisOptions getCollapseInstance() {
+ return COLLAPSE_INSTANCE;
+ }
+
+ public static IntraProceduralDataflowAnalysisOptions getNoCollapseInstance() {
+ return NO_COLLAPSE_INSTANCE;
+ }
+}
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 789a2cb..359456e 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -12,14 +13,29 @@
extends IntraProceduralDataflowAnalysisBase<BasicBlock, Instruction, StateType> {
public IntraproceduralDataflowAnalysis(
+ AppView<?> appView,
StateType bottom,
IRCode code,
AbstractTransferFunction<BasicBlock, Instruction, StateType> transfer) {
- super(bottom, code, transfer);
+ this(
+ appView,
+ bottom,
+ code,
+ transfer,
+ IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
+ }
+
+ public IntraproceduralDataflowAnalysis(
+ AppView<?> appView,
+ StateType bottom,
+ IRCode code,
+ AbstractTransferFunction<BasicBlock, Instruction, StateType> transfer,
+ IntraProceduralDataflowAnalysisOptions options) {
+ super(appView, bottom, code, transfer, options);
}
@Override
- boolean shouldCacheBlockEntryStateFor(BasicBlock block) {
+ boolean shouldCacheBlockEntryStateForNormalBlock(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
index 357ff33..dc0c539 100644
--- 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
@@ -6,9 +6,13 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -19,6 +23,9 @@
// The CfCode instruction index of the block's first instruction.
int firstInstructionIndex = -1;
+ // The CfCode instruction index of the block's first throwing instruction.
+ int firstThrowingInstructionIndex = -1;
+
// The CfCode instruction index of the block's last instruction.
int lastInstructionIndex = -1;
@@ -30,7 +37,7 @@
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<>();
+ final LinkedHashMap<DexType, CfBlock> exceptionalSuccessors = new LinkedHashMap<>();
public CfInstruction getFallthroughInstruction(CfCode code) {
int fallthroughInstructionIndex = getLastInstructionIndex() + 1;
@@ -43,6 +50,14 @@
return firstInstructionIndex;
}
+ public boolean hasThrowingInstruction() {
+ return firstThrowingInstructionIndex >= 0;
+ }
+
+ public int getFirstThrowingInstructionIndex() {
+ return firstThrowingInstructionIndex;
+ }
+
public CfInstruction getLastInstruction(CfCode code) {
return code.getInstructions().get(lastInstructionIndex);
}
@@ -55,18 +70,30 @@
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() {
+ public LinkedHashMap<DexType, CfBlock> getExceptionalSuccessors() {
return exceptionalSuccessors;
}
+ @Override
+ public String toString() {
+ List<String> predecessorStrings = new ArrayList<>();
+ predecessors.forEach(p -> predecessorStrings.add(p.getRangeString()));
+ exceptionalPredecessors.forEach(p -> predecessorStrings.add("*" + p.getRangeString()));
+ return "CfBlock(range="
+ + getRangeString()
+ + ", predecessors="
+ + StringUtils.join(", ", predecessorStrings)
+ + ")";
+ }
+
+ private String getRangeString() {
+ return firstInstructionIndex + "->" + lastInstructionIndex;
+ }
+
// A mutable interface for block construction.
static class MutableCfBlock extends CfBlock {
@@ -78,22 +105,35 @@
exceptionalPredecessors.add(block);
}
- void addExceptionalSuccessor(CfBlock block) {
- exceptionalSuccessors.add(block);
+ void addExceptionalSuccessor(CfBlock block, DexType guard) {
+ assert !exceptionalSuccessors.containsKey(guard);
+ exceptionalSuccessors.put(guard, block);
}
void setFirstInstructionIndex(int firstInstructionIndex) {
this.firstInstructionIndex = firstInstructionIndex;
}
+ void setFirstThrowingInstructionIndex(int firstThrowingInstructionIndex) {
+ this.firstThrowingInstructionIndex = firstThrowingInstructionIndex;
+ }
+
void setLastInstructionIndex(int lastInstructionIndex) {
this.lastInstructionIndex = lastInstructionIndex;
}
- boolean validate() {
+ boolean validate(CfControlFlowGraph cfg, InternalOptions options) {
assert 0 <= firstInstructionIndex;
assert firstInstructionIndex <= lastInstructionIndex;
+ assert firstThrowingInstructionIndex < 0
+ || firstInstructionIndex <= firstThrowingInstructionIndex;
+ assert firstThrowingInstructionIndex < 0
+ || firstThrowingInstructionIndex <= lastInstructionIndex;
assert SetUtils.newIdentityHashSet(predecessors).size() == predecessors.size();
+ assert this == cfg.getEntryBlock()
+ || !predecessors.isEmpty()
+ || !exceptionalPredecessors.isEmpty()
+ || options.getCfCodeAnalysisOptions().isUnreachableCfBlocksAllowed();
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
index ff69065..919e8d7 100644
--- 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
@@ -10,18 +10,22 @@
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.graph.DexType;
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.InternalOptions;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.TraversalUtils;
+import com.android.tools.r8.utils.TriFunction;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Collection;
+import java.util.Deque;
import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.BiFunction;
/**
@@ -48,8 +52,8 @@
return new Builder(code);
}
- public static CfControlFlowGraph create(CfCode code) {
- return builder(code).build();
+ public static CfControlFlowGraph create(CfCode code, InternalOptions options) {
+ return builder(code).build(options);
}
private CfBlock getBlock(CfInstruction blockEntry) {
@@ -58,8 +62,13 @@
}
@Override
+ public Collection<? extends CfBlock> getBlocks() {
+ return blocks.values();
+ }
+
+ @Override
public CfBlock getEntryBlock() {
- return getBlock(code.getInstructions().get(0));
+ return getBlock(code.getInstruction(0));
}
@Override
@@ -92,9 +101,12 @@
@Override
public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors(
CfBlock block,
- BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+ TriFunction<? super CfBlock, DexType, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
- return TraversalUtils.traverseIterable(block.getExceptionalSuccessors(), fn, initialValue);
+ return TraversalUtils.traverseMap(
+ block.getExceptionalSuccessors(),
+ (guard, exceptionalSuccessor, value) -> fn.apply(exceptionalSuccessor, guard, value),
+ initialValue);
}
@Override
@@ -107,7 +119,7 @@
for (int instructionIndex = block.getFirstInstructionIndex();
instructionIndex <= block.getLastInstructionIndex();
instructionIndex++) {
- CfInstruction instruction = code.getInstructions().get(instructionIndex);
+ CfInstruction instruction = code.getInstruction(instructionIndex);
traversalContinuation = fn.apply(instruction, traversalContinuation.asContinue().getValue());
if (traversalContinuation.shouldBreak()) {
break;
@@ -127,7 +139,7 @@
this.code = code;
}
- CfControlFlowGraph build() {
+ CfControlFlowGraph build(InternalOptions options) {
// Perform an initial pass over the CfCode to identify all instructions that start a new
// block.
createBlocks();
@@ -137,9 +149,11 @@
// identified all block entries up front.
processBlocks();
- assert blocks.values().stream().allMatch(MutableCfBlock::validate);
+ removeBlockForTrailingLabel();
- return new CfControlFlowGraph(blocks, code);
+ CfControlFlowGraph cfg = new CfControlFlowGraph(blocks, code);
+ assert blocks.values().stream().allMatch(block -> block.validate(cfg, options));
+ return cfg;
}
private void createBlocks() {
@@ -158,6 +172,11 @@
? instructions.get(fallthroughInstructionIndex)
: null;
instruction.forEachNormalTarget(this::createBlockIfAbsent, fallthroughInstruction);
+
+ // Also create a block for the fallthrough instruction, though it may be unreachable.
+ if (!instruction.asJump().hasFallthrough() && fallthroughInstruction != null) {
+ createBlockIfAbsent(fallthroughInstruction);
+ }
}
}
@@ -173,9 +192,9 @@
}
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 active catch handlers. The most recently added catch handlers take
+ // precedence over catch handlers added before them.
+ Deque<CfTryCatch> activeCatchHandlers = new ArrayDeque<>();
// 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.
@@ -199,12 +218,12 @@
instruction,
instructionIndex,
block,
- activeUntilCatchHandlers,
+ activeCatchHandlers,
inactiveUntilCatchHandlers);
}
}
- assert activeUntilCatchHandlers.isEmpty();
+ assert activeCatchHandlers.isEmpty();
assert inactiveUntilCatchHandlers.isEmpty();
}
@@ -212,31 +231,34 @@
CfInstruction instruction,
int instructionIndex,
MutableCfBlock block,
- Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers,
+ Deque<CfTryCatch> activeCatchHandlers,
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);
+ updateCatchHandlers(instruction.asLabel(), activeCatchHandlers, inactiveUntilCatchHandlers);
}
// Visit each instruction belonging to the current block.
- Set<CfLabel> exceptionalSuccessors = new LinkedHashSet<>();
+ Map<DexType, CfLabel> exceptionalSuccessors = new LinkedHashMap<>();
+ Iterator<CfTryCatch> activeCatchHandlerIterator = activeCatchHandlers.descendingIterator();
+ while (activeCatchHandlerIterator.hasNext()) {
+ CfTryCatch activeCatchHandler = activeCatchHandlerIterator.next();
+ activeCatchHandler.forEach(exceptionalSuccessors::putIfAbsent);
+ }
+
do {
assert !instruction.isLabel()
|| verifyCatchHandlersUnchanged(
- instruction.asLabel(), activeUntilCatchHandlers, inactiveUntilCatchHandlers);
- if (instruction.canThrow()) {
- for (CfTryCatch tryCatch : IterableUtils.flatten(activeUntilCatchHandlers.values())) {
- exceptionalSuccessors.addAll(tryCatch.getTargets());
- }
+ instruction.asLabel(), activeCatchHandlers, inactiveUntilCatchHandlers);
+ if (instruction.instructionTypeCanThrow() && !block.hasThrowingInstruction()) {
+ block.setFirstThrowingInstructionIndex(instructionIndex);
}
if (isBlockExit(instructionIndex)) {
break;
}
- instruction = code.getInstructions().get(++instructionIndex);
+ instruction = code.getInstruction(++instructionIndex);
} while (true);
// Record the index of the last instruction of the block.
@@ -249,15 +271,22 @@
// Add the current block as an exceptional predecessor of the exceptional successor blocks.
exceptionalSuccessors.forEach(
- exceptionalSuccessor -> {
+ (guard, exceptionalSuccessor) -> {
MutableCfBlock exceptionalSuccessorBlock = getBlock(exceptionalSuccessor);
- block.addExceptionalSuccessor(exceptionalSuccessorBlock);
+ block.addExceptionalSuccessor(exceptionalSuccessorBlock, guard);
exceptionalSuccessorBlock.addExceptionalPredecessor(block);
});
return instructionIndex;
}
+ private void removeBlockForTrailingLabel() {
+ CfInstruction lastInstruction = code.getInstruction(code.getInstructions().size() - 1);
+ if (lastInstruction.isLabel() && isBlockEntry(lastInstruction)) {
+ blocks.remove(lastInstruction);
+ }
+ }
+
private boolean isBlockEntry(CfInstruction instruction) {
return blocks.containsKey(instruction);
}
@@ -267,32 +296,35 @@
if (instructionIndex == lastInstructionIndex) {
return true;
}
- CfInstruction nextInstruction = code.getInstructions().get(instructionIndex + 1);
+ CfInstruction nextInstruction = code.getInstruction(instructionIndex + 1);
return isBlockEntry(nextInstruction);
}
private void updateCatchHandlers(
CfLabel instruction,
- Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers,
+ Deque<CfTryCatch> activeCatchHandlers,
Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers) {
// Remove active catch handlers that have expired at the current instruction.
- activeUntilCatchHandlers.remove(instruction);
+ activeCatchHandlers.removeIf(
+ activeCatchHandler -> activeCatchHandler.getEnd() == 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);
+ List<CfTryCatch> newlyActiveCatchHandlers = inactiveUntilCatchHandlers.remove(instruction);
+ if (newlyActiveCatchHandlers != null) {
+ for (int i = newlyActiveCatchHandlers.size() - 1; i >= 0; i--) {
+ CfTryCatch newlyActiveCatchHandler = newlyActiveCatchHandlers.get(i);
+ assert newlyActiveCatchHandler.getEnd() != newlyActiveCatchHandler.getStart();
+ activeCatchHandlers.addLast(newlyActiveCatchHandler);
+ }
}
}
private boolean verifyCatchHandlersUnchanged(
CfLabel instruction,
- Map<CfLabel, List<CfTryCatch>> activeUntilCatchHandlers,
+ Deque<CfTryCatch> activeCatchHandlers,
Map<CfLabel, List<CfTryCatch>> inactiveUntilCatchHandlers) {
- assert !activeUntilCatchHandlers.containsKey(instruction);
+ assert activeCatchHandlers.stream()
+ .allMatch(activeCatchHandler -> activeCatchHandler.getEnd() != instruction);
assert !inactiveUntilCatchHandlers.containsKey(instruction);
return true;
}
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
index b4ca696..cef6a5d 100644
--- 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
@@ -5,17 +5,25 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural.cf;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.AppView;
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;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisOptions;
public class CfIntraproceduralDataflowAnalysis<StateType extends AbstractState<StateType>>
extends IntraProceduralDataflowAnalysisBase<CfBlock, CfInstruction, StateType> {
public CfIntraproceduralDataflowAnalysis(
+ AppView<?> appView,
StateType bottom,
CfControlFlowGraph cfg,
AbstractTransferFunction<CfBlock, CfInstruction, StateType> transfer) {
- super(bottom, cfg, transfer);
+ super(
+ appView,
+ bottom,
+ cfg,
+ transfer,
+ IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
}
}
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 0d1bf75..432311b 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
@@ -26,6 +26,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.TriFunction;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
@@ -243,14 +244,17 @@
}
public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors(
- BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+ TriFunction<? super BasicBlock, DexType, ? 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));
+ fn.apply(
+ successors.get(i),
+ catchHandlers.getGuard(i),
+ traversalContinuation.asContinue().getValueOrDefault(null));
if (traversalContinuation.isBreak()) {
break;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 6005e1f..03771cd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -64,6 +64,10 @@
return guards;
}
+ public DexType getGuard(int index) {
+ return guards.get(index);
+ }
+
public List<T> getAllTargets() {
return targets;
}
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 8f310c8..dbcd9be 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
@@ -35,6 +35,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.TriFunction;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -1363,6 +1364,7 @@
}
}
+ @Override
public LinkedList<BasicBlock> getBlocks() {
return blocks;
}
@@ -1402,7 +1404,7 @@
@Override
public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors(
BasicBlock block,
- BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+ TriFunction<? super BasicBlock, DexType, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
return block.traverseExceptionalSuccessors(fn, initialValue);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index feaca12..7cb6f50 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractInstruction;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -46,7 +47,8 @@
import java.util.function.Function;
import java.util.function.Predicate;
-public abstract class Instruction implements InstructionOrPhi, TypeAndLocalInfoSupplier {
+public abstract class Instruction
+ implements AbstractInstruction, InstructionOrPhi, TypeAndLocalInfoSupplier {
protected Value outValue = null;
protected final List<Value> inValues = new ArrayList<>();
@@ -604,9 +606,8 @@
return false;
}
- /**
- * Returns true if this instruction may throw an exception.
- */
+ /** Returns true if this instruction may throw an exception. */
+ @Override
public boolean instructionTypeCanThrow() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 86fa768..4f28ee9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -637,8 +637,16 @@
}
private DexType convertUninitialized(FrameType type) {
- if (type.isInitialized()) {
- return type.getInitializedType(appView.dexItemFactory());
+ if (type.isInitializedReferenceType()) {
+ return type.asInitializedReferenceType().getInitializedType();
+ }
+ if (type.isPrimitive()) {
+ if (type.isSinglePrimitive()) {
+ return type.asSinglePrimitive().getInitializedType(appView.dexItemFactory());
+ } else {
+ assert type.isWidePrimitive();
+ return type.asWidePrimitive().getLowType().getInitializedType(appView.dexItemFactory());
+ }
}
if (type.isUninitializedNew()) {
int labelOffset = getLabelOffset(type.getUninitializedLabel());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 85ac399..70fddd9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -15,11 +15,14 @@
import static com.android.tools.r8.ir.analysis.type.TypeElement.getSingle;
import static com.android.tools.r8.ir.analysis.type.TypeElement.getWide;
-import com.android.tools.r8.ApiLevelException;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.ConstMethodHandleDiagnostic;
+import com.android.tools.r8.errors.ConstMethodTypeDiagnostic;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.errors.InvokePolymorphicMethodHandleDiagnostic;
+import com.android.tools.r8.errors.InvokePolymorphicVarHandleDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
@@ -121,7 +124,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -1224,10 +1227,9 @@
public void addConstMethodHandle(int dest, DexMethodHandle methodHandle) {
if (!appView.options().canUseConstantMethodHandle()) {
- throw new ApiLevelException(
- AndroidApiLevel.P,
- "Const-method-handle",
- null /* sourceString */);
+ throw appView
+ .reporter()
+ .fatalError(new ConstMethodHandleDiagnostic(origin, MethodPosition.create(method)));
}
TypeElement typeLattice =
TypeElement.fromDexType(
@@ -1239,10 +1241,9 @@
public void addConstMethodType(int dest, DexProto methodType) {
if (!appView.options().canUseConstantMethodType()) {
- throw new ApiLevelException(
- AndroidApiLevel.P,
- "Const-method-type",
- null /* sourceString */);
+ throw appView
+ .reporter()
+ .fatalError(new ConstMethodTypeDiagnostic(origin, MethodPosition.create(method)));
}
TypeElement typeLattice =
TypeElement.fromDexType(
@@ -1506,16 +1507,16 @@
if (type == Type.POLYMORPHIC) {
assert item instanceof DexMethod;
if (!appView.options().canUseInvokePolymorphic()) {
- throw new ApiLevelException(
- AndroidApiLevel.O,
- "MethodHandle.invoke and MethodHandle.invokeExact",
- null /* sourceString */);
+ throw appView
+ .reporter()
+ .fatalError(
+ new InvokePolymorphicMethodHandleDiagnostic(origin, MethodPosition.create(method)));
} else if (!appView.options().canUseInvokePolymorphicOnVarHandle()
&& ((DexMethod) item).holder == appView.dexItemFactory().varHandleType) {
- throw new ApiLevelException(
- AndroidApiLevel.P,
- "Call to polymorphic signature of VarHandle",
- null /* sourceString */);
+ throw appView
+ .reporter()
+ .fatalError(
+ new InvokePolymorphicVarHandleDiagnostic(origin, MethodPosition.create(method)));
}
}
add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
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 f94cde1..e504e3d 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
@@ -502,7 +502,7 @@
// The class file version is downgraded after compilation. Some of the desugaring might need
// the initial class file version to determine how far a method can be downgraded.
- if (clazz.hasClassFileVersion()) {
+ if (options.isGeneratingClassFiles() && clazz.hasClassFileVersion()) {
clazz.downgradeInitialClassFileVersion(
appView.options().classFileVersionAfterDesugaring(clazz.getInitialClassFileVersion()));
}
@@ -514,7 +514,7 @@
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
DexEncodedMethod definition = method.getDefinition();
- if (definition.hasClassFileVersion()) {
+ if (options.isGeneratingClassFiles() && definition.hasClassFileVersion()) {
definition.downgradeClassFileVersion(
appView.options().classFileVersionAfterDesugaring(definition.getClassFileVersion()));
}
@@ -552,7 +552,7 @@
if (options.testing.forceIRForCfToCfDesugar) {
return true;
}
- return !options.cfToCfDesugar;
+ return !options.isCfDesugaring();
}
private void checkPrefixMerging(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 324ecfa..73aeb93 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -2169,13 +2169,13 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
- new FrameType[] {FrameType.doubleType(), FrameType.doubleType()})),
+ new FrameType[] {FrameType.doubleType(), FrameType.doubleHighType()})),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
- new FrameType[] {FrameType.doubleType(), FrameType.doubleType()}),
+ new FrameType[] {FrameType.doubleType(), FrameType.doubleHighType()}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
label3),
@@ -2729,7 +2729,7 @@
FrameType.initialized(options.itemFactory.stringType),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 2),
new CfNumberConversion(NumericType.LONG, NumericType.INT),
@@ -2943,13 +2943,13 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfConstNumber(1, ValueType.LONG),
new CfReturn(ValueType.LONG),
@@ -2959,9 +2959,9 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
@@ -2978,9 +2978,9 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.INT),
@@ -3021,17 +3021,17 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType()))),
new CfConstNumber(0, ValueType.INT),
@@ -3041,17 +3041,17 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType(), FrameType.intType()))),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
@@ -3426,7 +3426,7 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.INT, 1),
label8,
@@ -3440,7 +3440,7 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfStore(ValueType.INT, 7),
@@ -3461,10 +3461,10 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 10),
@@ -3528,10 +3528,10 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.intType()
})),
@@ -3575,10 +3575,10 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.intType()
})),
@@ -3621,10 +3621,10 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.intType()
})),
@@ -3650,10 +3650,10 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 8),
new CfReturn(ValueType.LONG),
@@ -3752,13 +3752,13 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 2),
@@ -3770,9 +3770,9 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
@@ -3789,9 +3789,9 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.INT),
@@ -3832,17 +3832,17 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType()))),
new CfConstNumber(0, ValueType.LONG),
@@ -3852,17 +3852,17 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType(), FrameType.longType()))),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
@@ -3946,7 +3946,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.longType(), FrameType.longType(), FrameType.intType()
+ FrameType.longType(), FrameType.longHighType(), FrameType.intType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
@@ -3971,7 +3971,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.longType(), FrameType.longType(), FrameType.intType()
+ FrameType.longType(), FrameType.longHighType(), FrameType.intType()
})),
new CfLoad(ValueType.INT, 2),
new CfConstNumber(2, ValueType.INT),
@@ -3984,7 +3984,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.longType(), FrameType.longType(), FrameType.intType()
+ FrameType.longType(), FrameType.longHighType(), FrameType.intType()
})),
new CfConstNumber(10, ValueType.INT),
new CfStore(ValueType.INT, 2),
@@ -3993,7 +3993,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.longType(), FrameType.longType(), FrameType.intType()
+ FrameType.longType(), FrameType.longHighType(), FrameType.intType()
})),
new CfConstNumber(64, ValueType.INT),
new CfNewArray(options.itemFactory.charArrayType),
@@ -4031,7 +4031,7 @@
new int[] {0, 1, 2, 3, 4, 5, 6},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType(),
@@ -4075,7 +4075,7 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType()
@@ -4102,7 +4102,7 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType()
@@ -4127,12 +4127,12 @@
new int[] {0, 1, 2, 3, 4, 5, 6},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfLoad(ValueType.LONG, 5),
@@ -4168,14 +4168,14 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(0, ValueType.LONG),
@@ -4215,7 +4215,7 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.initialized(options.itemFactory.charArrayType),
FrameType.intType()
@@ -4285,7 +4285,7 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
@@ -4338,11 +4338,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfConstNumber(0, ValueType.INT),
label3,
@@ -4351,11 +4351,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfLoad(ValueType.LONG, 0),
@@ -4372,11 +4372,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfConstNumber(0, ValueType.INT),
@@ -4386,11 +4386,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType(), FrameType.intType()))),
new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
@@ -4404,11 +4404,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -4492,7 +4492,7 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
- new FrameType[] {FrameType.longType(), FrameType.longType()})),
+ new FrameType[] {FrameType.longType(), FrameType.longHighType()})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Sub, NumericType.LONG),
@@ -4631,13 +4631,13 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfConstNumber(1, ValueType.LONG),
new CfLoad(ValueType.LONG, 0),
@@ -4662,15 +4662,15 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 4),
label7,
@@ -4679,15 +4679,15 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType()))),
new CfReturn(ValueType.LONG),
@@ -4832,11 +4832,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfConstNumber(1, ValueType.LONG),
new CfLoad(ValueType.LONG, 0),
@@ -4859,13 +4859,13 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 4),
new CfLoad(ValueType.LONG, 2),
@@ -4876,13 +4876,13 @@
new int[] {0, 1, 2, 3, 4, 5, 6, 7},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.longType()))),
new CfReturn(ValueType.LONG),
@@ -4987,7 +4987,7 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
- new FrameType[] {FrameType.longType(), FrameType.longType()})),
+ new FrameType[] {FrameType.longType(), FrameType.longHighType()})),
new CfLoad(ValueType.LONG, 0),
new CfConstNumber(1, ValueType.LONG),
new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, NumericType.LONG),
@@ -5037,7 +5037,7 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
@@ -5142,9 +5142,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 4),
@@ -5162,9 +5162,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfConstNumber(0, ValueType.INT),
@@ -5174,9 +5174,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -5192,9 +5192,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -5205,9 +5205,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType(), FrameType.intType()))),
@@ -5235,12 +5235,12 @@
new int[] {0, 1, 2, 3, 4, 5, 6},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfLoad(ValueType.LONG, 5),
new CfReturn(ValueType.LONG),
@@ -5250,9 +5250,9 @@
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
@@ -5488,7 +5488,7 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
- new FrameType[] {FrameType.longType(), FrameType.longType()})),
+ new FrameType[] {FrameType.longType(), FrameType.longHighType()})),
new CfLoad(ValueType.LONG, 0),
new CfNeg(NumericType.LONG),
new CfReturn(ValueType.LONG),
@@ -5589,7 +5589,7 @@
FrameType.intType(),
FrameType.intType(),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.intType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
@@ -5642,11 +5642,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfConstNumber(0, ValueType.INT),
label3,
@@ -5655,11 +5655,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfLoad(ValueType.LONG, 0),
@@ -5676,11 +5676,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfConstNumber(0, ValueType.INT),
@@ -5690,11 +5690,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType(), FrameType.intType()))),
new CfLogicalBinop(CfLogicalBinop.Opcode.Or, NumericType.INT),
@@ -5708,11 +5708,11 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
+ FrameType.longHighType(),
FrameType.longType(),
- FrameType.longType(),
- FrameType.longType(),
- FrameType.longType()
+ FrameType.longHighType()
})),
new CfNew(options.itemFactory.createType("Ljava/lang/ArithmeticException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -5766,7 +5766,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.longType(), FrameType.longType(), FrameType.intType()
+ FrameType.longType(), FrameType.longHighType(), FrameType.intType()
})),
new CfLoad(ValueType.INT, 2),
new CfReturn(ValueType.INT),
@@ -9470,7 +9470,7 @@
FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
FrameType.initialized(options.itemFactory.objectType),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
})),
@@ -9503,7 +9503,7 @@
FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
FrameType.initialized(options.itemFactory.objectType),
FrameType.longType(),
- FrameType.longType(),
+ FrameType.longHighType(),
FrameType.initialized(options.itemFactory.objectType),
FrameType.initialized(options.itemFactory.objectType)
})),
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 00892fa..9788ee2 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
@@ -422,7 +422,7 @@
code.getInstructions(),
instruction ->
instruction.isFrame()
- ? instruction.asFrame().map(this::mapLookupTypeToObject)
+ ? instruction.asFrame().mapReferenceTypes(this::mapLookupTypeToObject)
: instruction);
return code.getInstructions() != newInstructions
? new CfCode(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
index 29ecfd2..0e6bd6a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.vivifiedTypeFor;
import com.android.tools.r8.cf.code.CfArrayLoad;
import com.android.tools.r8.cf.code.CfArrayStore;
@@ -156,9 +157,12 @@
computeReturnConversion(method, false, eventConsumer, context, contextSupplier);
DexMethod[] parameterConversions =
computeParameterConversions(method, true, eventConsumer, context, contextSupplier);
+ DexType newHolder =
+ appView.typeRewriter.hasRewrittenType(method.getHolderType(), appView)
+ ? vivifiedTypeFor(method.getHolderType(), appView)
+ : method.getHolderType();
DexMethod forwardMethod =
- convertedMethod(
- method, true, returnConversion, parameterConversions, wrapperField.getType());
+ convertedMethod(method, true, returnConversion, parameterConversions, newHolder);
CfCode cfCode =
new APIConversionCfCodeProvider(
appView,
@@ -467,12 +471,13 @@
private DexMethod internalComputeReturnConversion(
DexMethod invokedMethod,
- BiFunction<DexType, DexType, DexMethod> methodSupplier,
+ BiFunction<DexType, DexMethod, DexMethod> methodSupplier,
ProgramMethod context) {
DexType returnType = invokedMethod.proto.returnType;
- if (wrapperSynthesizer.shouldConvert(returnType, invokedMethod, context)) {
- DexType apiConversionCollection = getReturnApiConversionCollection(invokedMethod);
- return methodSupplier.apply(returnType, apiConversionCollection);
+ DexMethod apiGenericTypesConversion = getReturnApiGenericConversion(invokedMethod);
+ if (wrapperSynthesizer.shouldConvert(
+ returnType, apiGenericTypesConversion, invokedMethod, context)) {
+ return methodSupplier.apply(returnType, apiGenericTypesConversion);
}
return null;
}
@@ -486,9 +491,9 @@
return internalComputeParameterConversions(
invokedMethod,
wrapperSynthesizer,
- (argType, apiConversionCollection) ->
+ (argType, apiGenericTypesConversion) ->
wrapperSynthesizer.ensureConversionMethod(
- argType, destIsVivified, apiConversionCollection, eventConsumer, contextSupplier),
+ argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier),
context);
}
@@ -501,41 +506,42 @@
return internalComputeParameterConversions(
invokedMethod,
wrapperSynthesizer,
- (argType, apiConversionCollection) ->
+ (argType, apiGenericTypesConversion) ->
wrapperSynthesizer.getExistingProgramConversionMethod(
- argType, destIsVivified, apiConversionCollection, eventConsumer, contextSupplier),
+ argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier),
context);
}
private DexMethod[] internalComputeParameterConversions(
DexMethod invokedMethod,
DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
- BiFunction<DexType, DexType, DexMethod> methodSupplier,
+ BiFunction<DexType, DexMethod, DexMethod> methodSupplier,
ProgramMethod context) {
DexMethod[] parameterConversions = new DexMethod[invokedMethod.getArity()];
DexType[] parameters = invokedMethod.proto.parameters.values;
for (int i = 0; i < parameters.length; i++) {
- DexType apiConversionCollection = getApiConversionCollection(invokedMethod, i);
+ DexMethod apiGenericTypesConversion = getApiGenericConversion(invokedMethod, i);
DexType argType = parameters[i];
- if (wrapperSynthesizor.shouldConvert(argType, invokedMethod, context)) {
- parameterConversions[i] = methodSupplier.apply(argType, apiConversionCollection);
+ if (wrapperSynthesizor.shouldConvert(
+ argType, apiGenericTypesConversion, invokedMethod, context)) {
+ parameterConversions[i] = methodSupplier.apply(argType, apiGenericTypesConversion);
}
}
return parameterConversions;
}
- public DexType getReturnApiConversionCollection(DexMethod method) {
- return getApiConversionCollection(method, method.getArity());
+ public DexMethod getReturnApiGenericConversion(DexMethod method) {
+ return getApiGenericConversion(method, method.getArity());
}
- public DexType getApiConversionCollection(DexMethod method, int parameterIndex) {
- DexType[] dexTypes =
+ public DexMethod getApiGenericConversion(DexMethod method, int parameterIndex) {
+ DexMethod[] conversions =
appView
.options()
.machineDesugaredLibrarySpecification
- .getApiConversionCollection()
+ .getApiGenericConversion()
.get(method);
- return dexTypes == null ? null : dexTypes[parameterIndex];
+ return conversions == null ? null : conversions[parameterIndex];
}
private DexMethod convertedMethod(
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 a930b12..d28ccaf 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
@@ -33,7 +33,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider.ArrayConversionCfCodeProvider;
-import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider.CollectionConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.WrapperConstructorCfCodeProvider;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -118,13 +117,15 @@
|| appView.getSyntheticItems().isSyntheticOfKind(type, kinds -> kinds.VIVIFIED_WRAPPER);
}
- public boolean shouldConvert(DexType type, DexMethod method) {
- return shouldConvert(type, method, null);
- }
-
- public boolean shouldConvert(DexType type, DexMethod method, ProgramMethod context) {
+ public boolean shouldConvert(
+ DexType type, DexMethod apiGenericTypesConversion, DexMethod method, ProgramMethod context) {
if (type.isArrayType()) {
- return shouldConvert(type.toBaseType(appView.dexItemFactory()), method, context);
+ assert apiGenericTypesConversion == null;
+ return shouldConvert(
+ type.toBaseType(appView.dexItemFactory()), apiGenericTypesConversion, method, context);
+ }
+ if (apiGenericTypesConversion != null) {
+ return true;
}
if (!appView.typeRewriter.hasRewrittenType(type, appView)) {
return false;
@@ -139,13 +140,12 @@
public DexMethod ensureConversionMethod(
DexType type,
boolean destIsVivified,
- DexType apiConversionCollection,
+ DexMethod apiGenericTypesConversion,
DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer,
Supplier<UniqueContext> contextSupplier) {
- if (apiConversionCollection != null) {
+ if (apiGenericTypesConversion != null) {
assert !type.isArrayType();
- return ensureCollectionConversionMethod(
- type, destIsVivified, apiConversionCollection, eventConsumer, contextSupplier);
+ return apiGenericTypesConversion;
}
DexType srcType = destIsVivified ? type : vivifiedTypeFor(type);
DexType destType = destIsVivified ? vivifiedTypeFor(type) : type;
@@ -171,68 +171,6 @@
return conversion;
}
- private DexMethod ensureCollectionConversionMethod(
- DexType type,
- boolean destIsVivified,
- DexType apiConversionCollection,
- DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer,
- Supplier<UniqueContext> contextSupplier) {
- assert type == factory.setType || type == factory.listType;
- DexMethod conversion =
- ensureConversionMethod(
- apiConversionCollection,
- destIsVivified,
- null, // We do not support nested collections.
- eventConsumer,
- contextSupplier);
- return ensureCollectionConversionMethod(type, eventConsumer, contextSupplier, conversion);
- }
-
- private DexMethod ensureCollectionConversionMethodFromExistingBaseConversion(
- DexType type,
- boolean destIsVivified,
- DexType apiConversionCollection,
- DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
- Supplier<UniqueContext> contextSupplier) {
- assert type == factory.setType || type == factory.listType;
- DexMethod conversion =
- getExistingProgramConversionMethod(
- apiConversionCollection,
- destIsVivified,
- null, // We do not support nested collections.
- eventConsumer,
- contextSupplier);
- return ensureCollectionConversionMethod(type, eventConsumer, contextSupplier, conversion);
- }
-
- private DexMethod ensureCollectionConversionMethod(
- DexType collectionType,
- DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer,
- Supplier<UniqueContext> contextSupplier,
- DexMethod conversion) {
- ProgramMethod collectionConversion =
- appView
- .getSyntheticItems()
- .createMethod(
- kinds -> kinds.COLLECTION_CONVERSION,
- contextSupplier.get(),
- appView,
- builder ->
- builder
- .setProto(factory.createProto(collectionType, collectionType))
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(
- codeSynthesizor ->
- new CollectionConversionCfCodeProvider(
- appView,
- codeSynthesizor.getHolderType(),
- collectionType,
- conversion)
- .generateCfCode()));
- eventConsumer.acceptCollectionConversion(collectionConversion);
- return collectionConversion.getReference();
- }
-
private DexMethod ensureArrayConversionMethod(
DexType type,
DexType srcType,
@@ -300,13 +238,12 @@
public DexMethod getExistingProgramConversionMethod(
DexType type,
boolean destIsVivified,
- DexType apiConversionCollection,
+ DexMethod apiGenericTypesConversion,
DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
Supplier<UniqueContext> contextSupplier) {
- if (apiConversionCollection != null) {
+ if (apiGenericTypesConversion != null) {
assert !type.isArrayType();
- return ensureCollectionConversionMethodFromExistingBaseConversion(
- type, destIsVivified, apiConversionCollection, eventConsumer, contextSupplier);
+ return apiGenericTypesConversion;
}
DexType srcType = destIsVivified ? type : vivifiedTypeFor(type);
DexType destType = destIsVivified ? vivifiedTypeFor(type) : type;
@@ -699,7 +636,6 @@
.getWrappers()
.forEach(
(type, methods) -> {
- assert !librarySpecification.getCustomConversions().containsKey(type);
DexClass validClassToWrap = getValidClassToWrap(type);
// In broken set-ups we can end up having a json files containing wrappers of non
// desugared classes. Such wrappers are not required since the class won't be
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 189dca8..1aa5f62 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
@@ -48,7 +48,7 @@
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 API_CONVERSION_COLLECTION = "api_conversion_collection";
+ static final String API_GENERIC_TYPES_CONVERSION = "api_generic_types_conversion";
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";
@@ -264,15 +264,15 @@
builder.putDontRewritePrefix(dontRewritePrefix.getAsString());
}
}
- if (jsonFlagSet.has(API_CONVERSION_COLLECTION)) {
+ if (jsonFlagSet.has(API_GENERIC_TYPES_CONVERSION)) {
for (Map.Entry<String, JsonElement> methodAndDescription :
- jsonFlagSet.get(API_CONVERSION_COLLECTION).getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(API_GENERIC_TYPES_CONVERSION).getAsJsonObject().entrySet()) {
JsonArray array = methodAndDescription.getValue().getAsJsonArray();
for (int i = 0; i < array.size(); i += 2) {
- builder.addApiConversionCollection(
+ builder.addApiGenericTypesConversion(
parseMethod(methodAndDescription.getKey()),
array.get(i).getAsInt(),
- stringDescriptorToDexType(array.get(i + 1).getAsString()));
+ parseMethod(array.get(i + 1).getAsString()));
}
}
}
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 94ae8d0..f17235f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -15,13 +15,11 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
public class HumanRewritingFlags {
@@ -34,7 +32,7 @@
private final Map<DexMethod, DexType> covariantRetarget;
private final Map<DexMethod, DexType> retargetMethod;
private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
- private final Map<DexMethod, DexType[]> apiConversionCollection;
+ private final Map<DexMethod, DexMethod[]> apiGenericTypesConversion;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
private final Set<DexMethod> dontRewriteInvocation;
@@ -53,7 +51,7 @@
Map<DexMethod, DexType> covariantRetarget,
Map<DexMethod, DexType> retargetMethod,
Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
- Map<DexMethod, DexType[]> apiConversionCollection,
+ Map<DexMethod, DexMethod[]> apiGenericTypesConversion,
Map<DexType, DexType> legacyBackport,
Map<DexType, DexType> customConversion,
Set<DexMethod> dontRewriteInvocation,
@@ -70,7 +68,7 @@
this.covariantRetarget = covariantRetarget;
this.retargetMethod = retargetMethod;
this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch;
- this.apiConversionCollection = apiConversionCollection;
+ this.apiGenericTypesConversion = apiGenericTypesConversion;
this.legacyBackport = legacyBackport;
this.customConversions = customConversion;
this.dontRewriteInvocation = dontRewriteInvocation;
@@ -118,7 +116,7 @@
covariantRetarget,
retargetMethod,
retargetMethodEmulatedDispatch,
- apiConversionCollection,
+ apiGenericTypesConversion,
legacyBackport,
customConversions,
dontRewriteInvocation,
@@ -164,8 +162,8 @@
return retargetMethodEmulatedDispatch;
}
- public Map<DexMethod, DexType[]> getApiConversionCollection() {
- return apiConversionCollection;
+ public Map<DexMethod, DexMethod[]> getApiGenericConversion() {
+ return apiGenericTypesConversion;
}
public Map<DexType, DexType> getLegacyBackport() {
@@ -221,7 +219,7 @@
private final Map<DexMethod, DexType> covariantRetarget;
private final Map<DexMethod, DexType> retargetMethod;
private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
- private final Map<DexMethod, DexType[]> apiConversionCollection;
+ private final Map<DexMethod, DexMethod[]> apiGenericTypesConversion;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
private final Set<DexMethod> dontRewriteInvocation;
@@ -265,7 +263,7 @@
Map<DexMethod, DexType> covariantRetarget,
Map<DexMethod, DexType> retargetMethod,
Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
- Map<DexMethod, DexType[]> apiConversionCollection,
+ Map<DexMethod, DexMethod[]> apiConversionCollection,
Map<DexType, DexType> backportCoreLibraryMember,
Map<DexType, DexType> customConversions,
Set<DexMethod> dontRewriteInvocation,
@@ -284,7 +282,7 @@
this.covariantRetarget = new IdentityHashMap<>(covariantRetarget);
this.retargetMethod = new IdentityHashMap<>(retargetMethod);
this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch);
- this.apiConversionCollection = new IdentityHashMap<>(apiConversionCollection);
+ this.apiGenericTypesConversion = new IdentityHashMap<>(apiConversionCollection);
this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember);
this.customConversions = new IdentityHashMap<>(customConversions);
this.dontRewriteInvocation = Sets.newIdentityHashSet();
@@ -406,12 +404,13 @@
return this;
}
- public void addApiConversionCollection(DexMethod method, int index, DexType type) {
- DexType[] types =
- apiConversionCollection.computeIfAbsent(method, k -> new DexType[method.getArity() + 1]);
+ public void addApiGenericTypesConversion(DexMethod method, int index, DexMethod conversion) {
+ DexMethod[] types =
+ apiGenericTypesConversion.computeIfAbsent(
+ method, k -> new DexMethod[method.getArity() + 1]);
int actualIndex = index == -1 ? method.getArity() : index;
assert types[actualIndex] == null;
- types[actualIndex] = type;
+ types[actualIndex] = conversion;
}
public Builder putLegacyBackport(DexType backportType, DexType rewrittenBackportType) {
@@ -444,7 +443,6 @@
}
public HumanRewritingFlags build() {
- validate();
return new HumanRewritingFlags(
ImmutableMap.copyOf(rewritePrefix),
ImmutableSet.copyOf(dontRewritePrefix),
@@ -455,7 +453,7 @@
ImmutableMap.copyOf(covariantRetarget),
ImmutableMap.copyOf(retargetMethod),
ImmutableMap.copyOf(retargetMethodEmulatedDispatch),
- ImmutableMap.copyOf(apiConversionCollection),
+ ImmutableMap.copyOf(apiGenericTypesConversion),
ImmutableMap.copyOf(legacyBackport),
ImmutableMap.copyOf(customConversions),
ImmutableSet.copyOf(dontRewriteInvocation),
@@ -464,19 +462,5 @@
ImmutableMap.copyOf(amendLibraryMethod),
ImmutableMap.copyOf(amendLibraryField));
}
-
- private void validate() {
- SetView<DexType> dups =
- Sets.intersection(customConversions.keySet(), wrapperConversions.keySet());
- if (!dups.isEmpty()) {
- throw reporter.fatalError(
- new StringDiagnostic(
- "Invalid desugared library configuration. "
- + "Duplicate types in custom conversions and wrapper conversions: "
- + String.join(
- ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())),
- origin));
- }
- }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 4e08dc9..16b0b9f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -120,8 +120,8 @@
return rewritingFlags.getEmulatedVirtualRetargetThroughEmulatedInterface();
}
- public Map<DexMethod, DexType[]> getApiConversionCollection() {
- return rewritingFlags.getApiConversionCollection();
+ public Map<DexMethod, DexMethod[]> getApiGenericConversion() {
+ return rewritingFlags.getApiGenericConversion();
}
public void forEachRetargetMethod(Consumer<DexMethod> consumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index 7245ddc..e125c53 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -13,6 +14,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -35,7 +37,7 @@
Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface,
- Map<DexMethod, DexType[]> apiConversionCollection,
+ Map<DexMethod, DexMethod[]> apiGenericTypesConversion,
Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces,
Map<DexType, List<DexMethod>> wrappers,
Map<DexType, DexType> legacyBackport,
@@ -53,7 +55,7 @@
this.emulatedVirtualRetarget = emulatedVirtualRetarget;
this.emulatedVirtualRetargetThroughEmulatedInterface =
emulatedVirtualRetargetThroughEmulatedInterface;
- this.apiConversionCollection = apiConversionCollection;
+ this.apiGenericTypesConversion = apiGenericTypesConversion;
this.emulatedInterfaces = emulatedInterfaces;
this.wrappers = wrappers;
this.legacyBackport = legacyBackport;
@@ -94,7 +96,7 @@
private final Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface;
// Encodes weither specific parameter collections need to be wrapped differently.
- private final Map<DexMethod, DexType[]> apiConversionCollection;
+ private final Map<DexMethod, DexMethod[]> apiGenericTypesConversion;
// Emulated interface descriptors.
private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
@@ -144,8 +146,8 @@
return emulatedVirtualRetargetThroughEmulatedInterface;
}
- public Map<DexMethod, DexType[]> getApiConversionCollection() {
- return apiConversionCollection;
+ public Map<DexMethod, DexMethod[]> getApiGenericConversion() {
+ return apiGenericTypesConversion;
}
public void forEachRetargetMethod(Consumer<DexMethod> consumer) {
@@ -243,7 +245,7 @@
emulatedVirtualRetarget = ImmutableMap.builder();
private final ImmutableMap.Builder<DexMethod, DexMethod>
emulatedVirtualRetargetThroughEmulatedInterface = ImmutableMap.builder();
- private final ImmutableMap.Builder<DexMethod, DexType[]> apiConversionCollection =
+ private final ImmutableMap.Builder<DexMethod, DexMethod[]> apiGenericTypesConversion =
ImmutableMap.builder();
private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
ImmutableMap.builder();
@@ -302,8 +304,8 @@
emulatedVirtualRetargetThroughEmulatedInterface.put(src, dest);
}
- public void addApiConversionCollection(DexMethod method, DexType[] dexTypes) {
- apiConversionCollection.put(method, dexTypes);
+ public void addApiGenericTypesConversion(DexMethod method, DexMethod[] conversions) {
+ apiGenericTypesConversion.put(method, conversions);
}
public void addWrapper(DexType wrapperConversion, List<DexMethod> methods) {
@@ -334,10 +336,28 @@
return rewriteType.get(type);
}
+ private void validate(Set<DexType> maintainTypeBuilt) {
+ ArrayList<DexType> warnings = new ArrayList<>();
+ for (DexType toRewrite : rewriteType.keySet()) {
+ if (maintainTypeBuilt.contains(toRewrite)) {
+ warnings.add(toRewrite);
+ }
+ }
+ if (!warnings.isEmpty()) {
+ throw new CompilationError(
+ "The compilation cannot proceed because the desugared library specification contains"
+ + " ambiguous flags that the compiler cannot interpret: The following types are"
+ + " both rewritten and maintained "
+ + warnings);
+ }
+ }
+
public MachineRewritingFlags build() {
+ Set<DexType> maintainTypeBuilt = maintainType.build();
+ validate(maintainTypeBuilt);
return new MachineRewritingFlags(
rewriteType,
- maintainType.build(),
+ maintainTypeBuilt,
rewriteDerivedTypeOnly,
staticFieldRetarget.build(),
covariantRetarget.build(),
@@ -345,7 +365,7 @@
nonEmulatedVirtualRetarget.build(),
emulatedVirtualRetarget.build(),
emulatedVirtualRetargetThroughEmulatedInterface.build(),
- apiConversionCollection.build(),
+ apiGenericTypesConversion.build(),
emulatedInterfaces.build(),
wrappers.build(),
legacyBackport.build(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index dea440e..1097b06 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -97,7 +97,7 @@
ComputedApiLevel.unknown());
rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod);
rewritingFlags.getAmendLibraryField().forEach(builder::amendLibraryField);
- rewritingFlags.getApiConversionCollection().forEach(builder::addApiConversionCollection);
+ rewritingFlags.getApiGenericConversion().forEach(builder::addApiGenericTypesConversion);
new HumanToMachineRetargetConverter(appInfo)
.convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
new HumanToMachineEmulatedInterfaceConverter(appInfo)
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 2d360c0..d5168da 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
@@ -577,7 +577,7 @@
Set<DexType> emulatesInterfaces,
Map<DexType, GenericSignature.ClassTypeSignature> extraInterfaceSignatures) {
// TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
- if (appView.options().cfToCfDesugar && clazz.validInterfaceSignatures()) {
+ if (appView.options().isCfDesugaring() && clazz.validInterfaceSignatures()) {
clazz.forEachImmediateSupertypeWithSignature(
(type, signature) -> {
if (emulatesInterfaces.contains(type)) {
@@ -612,7 +612,7 @@
return;
}
// TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
- if (appView.options().cfToCfDesugar && clazz.validInterfaceSignatures()) {
+ if (appView.options().isCfDesugaring() && clazz.validInterfaceSignatures()) {
assert typeArguments != null;
clazz.forEachImmediateSupertypeWithAppliedTypeArguments(
typeArguments,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 89a9163..59dcd83 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -223,7 +223,7 @@
assert !appView.enableWholeProgramOptimizations();
DexProgramClass iface = method.getHolder();
if (method.getAccessFlags().isBridge()) {
- if (appView.options().cfToCfDesugar) {
+ if (appView.options().isCfDesugaring()) {
// TODO(b/187176895): Find the compilation causing this to not be removed.
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index bf34207..6551955 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -218,43 +218,47 @@
Instruction canonicalizedConstant = entry.getKey();
assert canonicalizedConstant.instructionTypeCanBeCanonicalized();
Instruction newConst;
- switch (canonicalizedConstant.opcode()) {
- case CONST_CLASS:
- if (Log.ENABLED) {
- numberOfConstClassCanonicalization++;
- }
- newConst = ConstClass.copyOf(code, canonicalizedConstant.asConstClass());
- break;
- case CONST_NUMBER:
- if (Log.ENABLED) {
- numberOfConstNumberCanonicalization++;
- }
- newConst = ConstNumber.copyOf(code, canonicalizedConstant.asConstNumber());
- break;
- case CONST_STRING:
- if (Log.ENABLED) {
- numberOfConstStringCanonicalization++;
- }
- newConst = ConstString.copyOf(code, canonicalizedConstant.asConstString());
- break;
- case DEX_ITEM_BASED_CONST_STRING:
- if (Log.ENABLED) {
- numberOfDexItemBasedConstStringCanonicalization++;
- }
- newConst =
- DexItemBasedConstString.copyOf(
- code, canonicalizedConstant.asDexItemBasedConstString());
- break;
- case STATIC_GET:
- if (Log.ENABLED) {
- numberOfEffectivelyFinalFieldCanonicalization++;
- }
- newConst = StaticGet.copyOf(code, canonicalizedConstant.asStaticGet());
- break;
- default:
- throw new Unreachable();
+ if (canonicalizedConstant.getBlock().isEntry()) {
+ newConst = canonicalizedConstant;
+ } else {
+ switch (canonicalizedConstant.opcode()) {
+ case CONST_CLASS:
+ if (Log.ENABLED) {
+ numberOfConstClassCanonicalization++;
+ }
+ newConst = ConstClass.copyOf(code, canonicalizedConstant.asConstClass());
+ break;
+ case CONST_NUMBER:
+ if (Log.ENABLED) {
+ numberOfConstNumberCanonicalization++;
+ }
+ newConst = ConstNumber.copyOf(code, canonicalizedConstant.asConstNumber());
+ break;
+ case CONST_STRING:
+ if (Log.ENABLED) {
+ numberOfConstStringCanonicalization++;
+ }
+ newConst = ConstString.copyOf(code, canonicalizedConstant.asConstString());
+ break;
+ case DEX_ITEM_BASED_CONST_STRING:
+ if (Log.ENABLED) {
+ numberOfDexItemBasedConstStringCanonicalization++;
+ }
+ newConst =
+ DexItemBasedConstString.copyOf(
+ code, canonicalizedConstant.asDexItemBasedConstString());
+ break;
+ case STATIC_GET:
+ if (Log.ENABLED) {
+ numberOfEffectivelyFinalFieldCanonicalization++;
+ }
+ newConst = StaticGet.copyOf(code, canonicalizedConstant.asStaticGet());
+ break;
+ default:
+ throw new Unreachable();
+ }
+ insertCanonicalizedConstant(code, newConst);
}
- insertCanonicalizedConstant(code, newConst);
for (Value outValue : entry.getValue()) {
outValue.replaceUsers(newConst.outValue());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
index f45344f..8842317 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -26,7 +26,7 @@
// Analyze code.
IntraproceduralDataflowAnalysis<ParameterUsages> analysis =
new IntraproceduralDataflowAnalysis<>(
- ParameterUsages.bottom(), code, new TransferFunction(appView, method, code));
+ appView, ParameterUsages.bottom(), code, new TransferFunction(appView, method, code));
SuccessfulDataflowAnalysisResult<?, ParameterUsages> result =
timing.time(
"Data flow analysis",
@@ -34,7 +34,7 @@
if (result == null) {
return ClassInlinerMethodConstraint.alwaysFalse();
}
- ParameterUsages usages = timing.time("Externalize", () -> result.join().externalize());
+ ParameterUsages usages = timing.time("Externalize", () -> result.join(appView).externalize());
if (usages.isBottom()) {
return ClassInlinerMethodConstraint.alwaysTrue();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
index ac81581..f4a45a8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.classinliner.analysis;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
import com.android.tools.r8.utils.IntObjToObjFunction;
@@ -42,7 +43,7 @@
}
@Override
- public ParameterUsages join(ParameterUsages state) {
+ public ParameterUsages join(AppView<?> appView, ParameterUsages state) {
if (isBottom()) {
return state;
}
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 e4bbb6c..f6bc6e6 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
@@ -149,6 +149,16 @@
return predecessorExitState;
}
+ @Override
+ public ParameterUsages computeExceptionalBlockEntryState(
+ BasicBlock block,
+ DexType guard,
+ BasicBlock throwBlock,
+ Instruction throwInstruction,
+ ParameterUsages throwState) {
+ return throwState;
+ }
+
private ParameterUsages analyzeArgument(Argument argument, ParameterUsages state) {
// Only consider arguments that could store an instance eligible for class inlining. Note that
// we can't ignore parameters with a library type, since instances of program classes could
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index df2c436..0c41415 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexItemFactory.EnumMembers;
@@ -14,7 +13,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
@@ -87,16 +85,6 @@
}
}
- private void modelStaticFinalLibraryFields(Set<DexEncodedField> finalLibraryFields) {
- for (DexEncodedField field : finalLibraryFields) {
- if (field.isStatic()) {
- feedback.recordLibraryFieldHasAbstractValue(
- field,
- abstractValueFactory.createSingleFieldValue(field.getReference(), ObjectState.empty()));
- }
- }
- }
-
private void modelLibraryMethodsNonNullParamOrThrow() {
dexItemFactory.libraryMethodsNonNullParamOrThrow.forEach(
(method, nonNullParamOrThrow) -> {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
new file mode 100644
index 0000000..5ecb6bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import com.google.common.collect.ImmutableList;
+
+/** StringBuilderAction defines an interface for updating the IR code based on optimizations. */
+public interface StringBuilderAction {
+
+ void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle);
+
+ /** The RemoveStringBuilderAction will simply remove the instruction completely. */
+ class RemoveStringBuilderAction implements StringBuilderAction {
+
+ private static final RemoveStringBuilderAction INSTANCE = new RemoveStringBuilderAction();
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ assert oracle.isModeledStringBuilderInstruction(instruction);
+ if (oracle.isAppend(instruction) && instruction.outValue() != null) {
+ // Append will return the string builder instance. Before removing, ensure that
+ // all users of the output values uses the receiver.
+ instruction.outValue().replaceUsers(instruction.getFirstOperand());
+ }
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
+
+ static RemoveStringBuilderAction getInstance() {
+ return INSTANCE;
+ }
+ }
+
+ /**
+ * ReplaceByConstantString will replace a toString() call on StringBuilder with a constant string.
+ */
+ class ReplaceByConstantString implements StringBuilderAction {
+
+ private final String replacement;
+
+ ReplaceByConstantString(String replacement) {
+ this.replacement = replacement;
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ assert oracle.isToString(instruction);
+ iterator.replaceCurrentInstructionWithConstString(appView, code, replacement);
+ }
+ }
+
+ /**
+ * AppendWithNewConstantString will change the current instruction to be an append with a constant
+ * string. If the current instruction is init the instruction will be changed to an init taking a
+ * string as argument.
+ */
+ class AppendWithNewConstantString implements StringBuilderAction {
+
+ private final String replacement;
+
+ AppendWithNewConstantString(String replacement) {
+ this.replacement = replacement;
+ }
+
+ @Override
+ public void perform(
+ AppView<?> appView,
+ IRCode code,
+ InstructionListIterator iterator,
+ Instruction instruction,
+ StringBuilderOracle oracle) {
+ Instruction previous = iterator.previous();
+ InvokeMethodWithReceiver invoke = previous.asInvokeMethodWithReceiver();
+ assert invoke != null;
+ // If the block has catch handlers, inserting a constant string in the same block as the
+ // append violates our block representation in DEX since constant string is throwing. If the
+ // append is in a block with catch handlers, we simply insert a new constant string in the
+ // entry block after all arguments.
+ Value value;
+ if (!invoke.getBlock().hasCatchHandlers()) {
+ value =
+ iterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(replacement));
+ } else {
+ InstructionListIterator stringInsertIterator = code.entryBlock().listIterator(code);
+ while (stringInsertIterator.hasNext()) {
+ Instruction next = stringInsertIterator.next();
+ if (!next.isArgument()) {
+ stringInsertIterator.previous();
+ break;
+ }
+ }
+ value =
+ stringInsertIterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(replacement));
+ }
+ iterator.next();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (invoke.isInvokeConstructor(appView.dexItemFactory())) {
+ iterator.replaceCurrentInstruction(
+ InvokeDirect.builder()
+ .setArguments(ImmutableList.of(invoke.getReceiver(), value))
+ .setMethod(
+ getConstructorWithStringParameter(invokedMethod, appView.dexItemFactory()))
+ .setOutValue(invoke.outValue())
+ .build());
+ } else if (!isAppendWithString(invokedMethod, appView.dexItemFactory())) {
+ iterator.replaceCurrentInstruction(
+ InvokeVirtual.builder()
+ .setArguments(ImmutableList.of(invoke.getReceiver(), value))
+ .setMethod(getAppendWithStringParameter(invokedMethod, appView.dexItemFactory()))
+ .setOutValue(invoke.outValue())
+ .build());
+ } else {
+ invoke.replaceValue(1, value);
+ }
+ }
+
+ private boolean isAppendWithString(DexMethod method, DexItemFactory factory) {
+ return factory.stringBufferMethods.isAppendStringMethod(method)
+ || factory.stringBuilderMethods.isAppendStringMethod(method);
+ }
+
+ private DexMethod getConstructorWithStringParameter(
+ DexMethod invokedMethod, DexItemFactory factory) {
+ if (invokedMethod.getHolderType() == factory.stringBufferType) {
+ return factory.stringBufferMethods.stringConstructor;
+ } else {
+ assert invokedMethod.getHolderType() == factory.stringBuilderType;
+ return factory.stringBuilderMethods.stringConstructor;
+ }
+ }
+
+ private DexMethod getAppendWithStringParameter(
+ DexMethod invokedMethod, DexItemFactory factory) {
+ if (invokedMethod.getHolderType() == factory.stringBufferType) {
+ return factory.stringBufferMethods.appendString;
+ } else {
+ assert invokedMethod.getHolderType() == factory.stringBuilderType;
+ return factory.stringBuilderMethods.appendString;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
index 1179c6d..bb75dc2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.string;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
@@ -40,10 +41,16 @@
* loop.
*/
static boolean hasAppendInstructionInLoop(
- IRCode code, Value builder, StringBuilderOptimizationConfiguration configuration) {
+ AppView<?> appView,
+ IRCode code,
+ Value builder,
+ StringBuilderOptimizationConfiguration configuration) {
IntraproceduralDataflowAnalysis<AbstractStateImpl> analysis =
new IntraproceduralDataflowAnalysis<>(
- AbstractStateImpl.bottom(), code, new TransferFunction(builder, configuration));
+ appView,
+ AbstractStateImpl.bottom(),
+ code,
+ new TransferFunction(builder, configuration));
DataflowAnalysisResult result = analysis.run(builder.definition.getBlock());
return result.isFailedAnalysisResult();
}
@@ -86,7 +93,7 @@
}
@Override
- public AbstractStateImpl join(AbstractStateImpl state) {
+ public AbstractStateImpl join(AppView<?> appView, AbstractStateImpl state) {
if (liveAppendInstructions.isEmpty()) {
return state;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
new file mode 100644
index 0000000..caf8432
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -0,0 +1,562 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.ir.optimize.string.StringBuilderHelper.canMutate;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createAppendNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createEscapeNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createImplicitToStringNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createInitNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createInspectionNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createMutateNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createNewInstanceNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createOtherStringBuilderNode;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderNode.createToStringNode;
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisOptions;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraproceduralDataflowAnalysis;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.EscapeNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.LoopNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNodeMuncher.MunchingState;
+import com.android.tools.r8.ir.optimize.string.StringBuilderOracle.DefaultStringBuilderOracle;
+import com.android.tools.r8.utils.DepthFirstSearchWorkListBase.DepthFirstSearchWorkList;
+import com.android.tools.r8.utils.DepthFirstSearchWorkListBase.StatefulDepthFirstSearchWorkList;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+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.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * {@link StringBuilderAppendOptimizer} will try to optimize string builders by joining appends with
+ * constant arguments and materializing toStrings into constant strings to be able to remove entire
+ * StringBuilders (or StringBuffers).
+ *
+ * <p>The algorithm works by producing graphs for all string builders of a method. The graphs follow
+ * the control flow of the method, thus, if a string builder is assigned a value in two blocks from
+ * a certain point, and there is no linear path between the two assignments, they will show up in a
+ * graph as two successor nodes to a common predecessor. StringBuilder graphs are not tracked
+ * through phis, instead a phi and subsequent uses of that phi will also occur as a graph.
+ *
+ * <p>When all graphs are constructed the graphs are optimized by running a munching algorithm on
+ * them.
+ *
+ * <p>Finally, based on all optimizations, the IR is updated to reflect the optimizations.
+ */
+public class StringBuilderAppendOptimizer {
+
+ private final AppView<?> appView;
+ private final StringBuilderOracle oracle;
+ private final IRCode code;
+
+ private static final int NUMBER_OF_MUNCHING_PASSES = 3;
+
+ private StringBuilderAppendOptimizer(AppView<?> appView, IRCode code) {
+ this.appView = appView;
+ this.code = code;
+ oracle = new DefaultStringBuilderOracle(appView.dexItemFactory());
+ }
+
+ public static void run(AppView<?> appView, IRCode code) {
+ new StringBuilderAppendOptimizer(appView, code).run();
+ }
+
+ private void run() {
+ Map<Value, StringBuilderNode> stringBuilderGraphs = computeStringBuilderGraphs();
+ Map<Instruction, StringBuilderAction> actions = optimizeOnGraphs(stringBuilderGraphs);
+ if (actions.isEmpty()) {
+ return;
+ }
+ InstructionListIterator it = code.instructionListIterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ StringBuilderAction stringBuilderAction = actions.get(instruction);
+ if (stringBuilderAction != null) {
+ stringBuilderAction.perform(appView, code, it, instruction, oracle);
+ }
+ }
+ }
+
+ private static class StringBuilderGraphState {
+
+ private final Map<Value, StringBuilderNode> roots;
+ private final Map<Value, StringBuilderNode> tails;
+ private boolean isPartOfLoop;
+
+ private StringBuilderGraphState(
+ Map<Value, StringBuilderNode> roots, Map<Value, StringBuilderNode> tails) {
+ this.roots = roots;
+ this.tails = tails;
+ }
+ }
+
+ /***
+ * This will compute a collection of graphs from StringBuilders and return a map from value to
+ * the root of the string builder graph.
+ *
+ * The graphs are constructed by doing a DFS traversel and computing all string builder actions
+ * inside a block. The flow here is known to be linear. When backtracking, the linear collection
+ * of nodes (represented by root and tail) are then combined in a way that ensure the control flow
+ * of the method is maintained in the graph.
+ *
+ * To ensure that we correctly compute when a string builder escapes and when it can be mutated
+ * we first compute a {@link StringBuilderEscapeState} by using a flow analysis, enabling us to
+ * answer, for a given instruction, is a string builder value escaping and all escaped values
+ * at the instruction.
+ */
+ private Map<Value, StringBuilderNode> computeStringBuilderGraphs() {
+ StringBuilderEscapeTransferFunction transferFunction =
+ new StringBuilderEscapeTransferFunction(oracle);
+ IntraproceduralDataflowAnalysis<StringBuilderEscapeState> analysis =
+ new IntraproceduralDataflowAnalysis<>(
+ appView,
+ StringBuilderEscapeState.bottom(),
+ code,
+ transferFunction,
+ IntraProceduralDataflowAnalysisOptions.getNoCollapseInstance());
+ SuccessfulDataflowAnalysisResult<?, StringBuilderEscapeState> stringBuilderEscapeResult =
+ analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
+
+ if (stringBuilderEscapeResult == null) {
+ return Collections.emptyMap();
+ }
+
+ TraversalContinuation<Void, StringBuilderGraphState> graphResult =
+ new StatefulDepthFirstSearchWorkList<BasicBlock, StringBuilderGraphState, Void>() {
+
+ @Override
+ @SuppressWarnings("ReturnValueIgnored")
+ protected TraversalContinuation<Void, StringBuilderGraphState> process(
+ DFSNodeWithState<BasicBlock, StringBuilderGraphState> node,
+ Function<BasicBlock, DFSNodeWithState<BasicBlock, StringBuilderGraphState>>
+ childNodeConsumer) {
+ Map<Value, StringBuilderNode> currentRoots = new IdentityHashMap<>();
+ Map<Value, StringBuilderNode> currentTails = new IdentityHashMap<>();
+ BasicBlock block = node.getNode();
+ StringBuilderEscapeState previousState = analysis.computeBlockEntryState(block);
+ TransferFunctionResult<StringBuilderEscapeState> result =
+ transferFunction.applyBlock(block, previousState);
+ if (result.isFailedTransferResult()) {
+ assert false : "Computing the state should never fail";
+ return TraversalContinuation.doBreak();
+ }
+ previousState = result.asAbstractState();
+ for (Phi phi : block.getPhis()) {
+ if (previousState.isLiveStringBuilder(phi)) {
+ visitAllAliasing(
+ phi,
+ previousState,
+ value -> {},
+ alias -> {
+ EscapeNode escapeNode = new EscapeNode();
+ currentRoots.put(alias, escapeNode);
+ currentTails.put(alias, escapeNode);
+ });
+ }
+ }
+ for (Instruction instruction : block.getInstructions()) {
+ result = transferFunction.apply(instruction, previousState);
+ if (result.isFailedTransferResult()) {
+ assert false : "Computing the state should never fail";
+ return TraversalContinuation.doBreak();
+ }
+ previousState = result.asAbstractState();
+ createNodesForInstruction(
+ instruction,
+ previousState,
+ (value, sbNode) -> {
+ StringBuilderNode currentTail = currentTails.get(value);
+ if (currentTail == null) {
+ currentRoots.put(value, sbNode);
+ currentTails.put(value, sbNode);
+ } else if (shouldAddNodeToGraph(currentTail, sbNode)) {
+ currentTail.addSuccessor(sbNode);
+ currentTails.put(value, sbNode);
+ }
+ });
+ }
+ assert currentRoots.keySet().equals(currentTails.keySet());
+ assert previousState.getLiveStringBuilders().containsAll(currentRoots.keySet())
+ : "Seen root that is not a live string builder";
+ node.setState(new StringBuilderGraphState(currentRoots, currentTails));
+ for (BasicBlock successor : block.getSuccessors()) {
+ childNodeConsumer.apply(successor);
+ }
+ return TraversalContinuation.doContinue();
+ }
+
+ private boolean shouldAddNodeToGraph(
+ StringBuilderNode insertedNode, StringBuilderNode newNode) {
+ // No need for multiple mutating nodes or inspecting nodes.
+ if (insertedNode.isMutateNode()) {
+ return !newNode.isMutateNode() && !newNode.isInspectingNode();
+ } else if (insertedNode.isInspectingNode()) {
+ return !newNode.isInspectingNode();
+ }
+ return true;
+ }
+
+ private void createNodesForInstruction(
+ Instruction instruction,
+ StringBuilderEscapeState escapeState,
+ BiConsumer<Value, StringBuilderNode> nodeConsumer) {
+ // Do not build nodes for assume values.
+ if (instruction.isAssume()) {
+ return;
+ }
+ if (oracle.isModeledStringBuilderInstruction(instruction)) {
+ createNodesForStringBuilderInstruction(instruction, escapeState, nodeConsumer);
+ } else {
+ for (Value newEscapedValue : escapeState.getNewlyEscaped()) {
+ visitStringBuilderValues(
+ newEscapedValue,
+ escapeState,
+ nonAlias -> nodeConsumer.accept(nonAlias, createEscapeNode()),
+ alias -> nodeConsumer.accept(alias, createEscapeNode()));
+ }
+ if (canMutate(instruction)) {
+ for (Value escapedStringBuilder : escapeState.getEscaping()) {
+ visitStringBuilderValues(
+ escapedStringBuilder,
+ escapeState,
+ nonAlias -> nodeConsumer.accept(nonAlias, createMutateNode()),
+ alias -> nodeConsumer.accept(alias, createMutateNode()));
+ }
+ }
+ }
+ }
+
+ private void createNodesForStringBuilderInstruction(
+ Instruction instruction,
+ StringBuilderEscapeState escapeState,
+ BiConsumer<Value, StringBuilderNode> nodeConsumer) {
+ if (instruction.isNewInstance()) {
+ Value newInstanceValue = instruction.outValue();
+ assert newInstanceValue != null;
+ nodeConsumer.accept(
+ newInstanceValue, createNewInstanceNode(instruction.asNewInstance()));
+ } else {
+ InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+ Value receiver = invoke.getReceiver();
+ if (oracle.isInit(instruction)) {
+ InitNode initNode = createInitNode(instruction.asInvokeDirect());
+ initNode.setConstantArgument(oracle.getConstantArgument(instruction));
+ if (invoke.arguments().size() == 2) {
+ Value arg = invoke.getOperand(1);
+ if (oracle.hasStringBuilderType(arg)) {
+ insertImplicitToStringNode(
+ arg, instruction, initNode, escapeState, nodeConsumer);
+ }
+ }
+ visitStringBuilderValues(
+ receiver,
+ escapeState,
+ actual -> nodeConsumer.accept(actual, initNode),
+ escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
+ } else if (oracle.isAppend(instruction)) {
+ AppendNode appendNode = createAppendNode(instruction.asInvokeVirtual());
+ appendNode.setConstantArgument(oracle.getConstantArgument(instruction));
+ Value arg = invoke.getOperand(1).getAliasedValue();
+ if (oracle.hasStringBuilderType(arg)) {
+ insertImplicitToStringNode(
+ arg, instruction, appendNode, escapeState, nodeConsumer);
+ }
+ visitStringBuilderValues(
+ receiver,
+ escapeState,
+ actual -> nodeConsumer.accept(actual, appendNode),
+ escaped -> nodeConsumer.accept(escaped, createMutateNode()));
+ } else if (oracle.isToString(instruction)) {
+ visitStringBuilderValues(
+ receiver,
+ escapeState,
+ actual ->
+ nodeConsumer.accept(
+ actual, createToStringNode(instruction.asInvokeVirtual())),
+ escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
+ } else if (oracle.isInspecting(instruction)) {
+ visitStringBuilderValues(
+ receiver,
+ escapeState,
+ actual -> nodeConsumer.accept(actual, createInspectionNode(instruction)),
+ escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
+ } else {
+ visitStringBuilderValues(
+ receiver,
+ escapeState,
+ actual ->
+ nodeConsumer.accept(actual, createOtherStringBuilderNode(instruction)),
+ escaped ->
+ nodeConsumer.accept(escaped, createOtherStringBuilderNode(instruction)));
+ }
+ }
+ }
+
+ // For tracking propagating constant string builder values through append or init we
+ // build a link between the two graphs.
+ private void insertImplicitToStringNode(
+ Value value,
+ Instruction instruction,
+ InitOrAppend node,
+ StringBuilderEscapeState escapeState,
+ BiConsumer<Value, StringBuilderNode> nodeConsumer) {
+ assert escapeState.isLiveStringBuilder(value);
+ ImplicitToStringNode implicitToStringNode = createImplicitToStringNode(node);
+ visitStringBuilderValues(
+ value,
+ escapeState,
+ actual -> nodeConsumer.accept(actual, implicitToStringNode),
+ escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
+ node.setImplicitToStringNode(implicitToStringNode);
+ }
+
+ private void visitStringBuilderValues(
+ Value value,
+ StringBuilderEscapeState state,
+ Consumer<Value> actualConsumer,
+ Consumer<Value> aliasAndEscapedConsumer) {
+ assert state.isLiveStringBuilder(value);
+ boolean seenEscaped =
+ visitAllAliasing(value, state, actualConsumer, aliasAndEscapedConsumer);
+ seenEscaped |= visitAllAliases(value, state, aliasAndEscapedConsumer);
+ if (seenEscaped) {
+ for (Value escapingValue : state.getEscaping()) {
+ aliasAndEscapedConsumer.accept(escapingValue);
+ }
+ }
+ }
+
+ private boolean visitAllAliasing(
+ Value value,
+ StringBuilderEscapeState state,
+ Consumer<Value> nonAliasConsumer,
+ Consumer<Value> aliasAndEscapedConsumer) {
+ WorkList<Value> valueWorkList = WorkList.newIdentityWorkList(value);
+ boolean seenUnknownAlias = false;
+ boolean seenEscaped = false;
+ while (valueWorkList.hasNext()) {
+ Value next = valueWorkList.next();
+ seenEscaped |= state.isEscaped(next);
+ Set<Value> aliasing =
+ state.getAliasesToDefinitions().getOrDefault(next, Collections.emptySet());
+ valueWorkList.addIfNotSeen(aliasing);
+ // Check if we have a direct alias such as Assume, CheckCast or StringBuilder.append.
+ if (aliasing.size() != 1 || next.isPhi()) {
+ if (!seenUnknownAlias) {
+ nonAliasConsumer.accept(next);
+ seenUnknownAlias = true;
+ } else {
+ aliasAndEscapedConsumer.accept(next);
+ }
+ }
+ }
+ return seenEscaped;
+ }
+
+ private boolean visitAllAliases(
+ Value value, StringBuilderEscapeState state, Consumer<Value> aliasConsumer) {
+ Map<Value, Set<Value>> escapedDefinitionsToKnown = state.getDefinitionsToAliases();
+ WorkList<Value> valueWorkList =
+ WorkList.newIdentityWorkList(
+ escapedDefinitionsToKnown.getOrDefault(value, Collections.emptySet()));
+ boolean seenEscaped = false;
+ while (valueWorkList.hasNext()) {
+ Value next = valueWorkList.next();
+ seenEscaped |= state.isEscaped(next);
+ if (next.isPhi()) {
+ aliasConsumer.accept(next);
+ }
+ valueWorkList.addIfNotSeen(
+ escapedDefinitionsToKnown.getOrDefault(next, Collections.emptySet()));
+ }
+ return seenEscaped;
+ }
+
+ @Override
+ protected TraversalContinuation<Void, StringBuilderGraphState> joiner(
+ DFSNodeWithState<BasicBlock, StringBuilderGraphState> node,
+ List<DFSNodeWithState<BasicBlock, StringBuilderGraphState>> childStates) {
+ StringBuilderGraphState state = node.getState();
+ for (DFSNodeWithState<BasicBlock, StringBuilderGraphState> childState : childStates) {
+ StringBuilderGraphState childGraphState = childState.getState();
+ childGraphState.roots.forEach(
+ (value, sbNode) -> {
+ StringBuilderNode currentRoot = state.roots.get(value);
+ StringBuilderNode currentTail = state.tails.get(value);
+ if (currentRoot == null) {
+ assert currentTail == null;
+ if (childStates.size() == 1) {
+ state.roots.put(value, sbNode);
+ state.tails.put(value, sbNode);
+ return;
+ }
+ // We are adding a value coming only from a child state and not defined here.
+ // To ensure proper ordering we add a sentinel node. If it turns out there is
+ // only a single reference, we rely on munching to remove it.
+ currentRoot = StringBuilderNode.createSplitReferenceNode();
+ currentTail = currentRoot;
+ state.roots.put(value, currentRoot);
+ state.tails.put(value, currentTail);
+ }
+ assert currentTail != null;
+ // Link next node from successor
+ currentTail.addSuccessor(sbNode);
+ sbNode.addPredecessor(currentTail);
+ });
+ if (childState.seenAndNotProcessed()) {
+ childGraphState.isPartOfLoop = true;
+ }
+ }
+ if (state.isPartOfLoop) {
+ for (Value value : state.roots.keySet()) {
+ LoopNode loopNode = StringBuilderNode.createLoopNode();
+ loopNode.addSuccessor(state.roots.get(value));
+ state.roots.put(value, loopNode);
+ }
+ }
+ return TraversalContinuation.doContinue(state);
+ }
+ }.run(code.entryBlock());
+
+ return graphResult.shouldBreak()
+ ? Collections.emptyMap()
+ : graphResult.asContinue().getValue().roots;
+ }
+
+ /**
+ * optimizeOnGraphs will compute some state that will make munching easier. When computing the
+ * state the search will also do a topological sort over string builder references such that
+ * string builders without a direct dependency on another string builder will be computed first.
+ *
+ * <p>In general, this would not really matter since we munch over all graphs, but we are limiting
+ * the munching and care about performance.
+ */
+ private Map<Instruction, StringBuilderAction> optimizeOnGraphs(
+ Map<Value, StringBuilderNode> stringBuilderGraphs) {
+ Map<Instruction, StringBuilderAction> actions = new IdentityHashMap<>();
+ // Build state to allow munching over the string builder graphs.
+ Set<StringBuilderNode> inspectingCapacity = Sets.newIdentityHashSet();
+ Set<StringBuilderNode> looping = Sets.newIdentityHashSet();
+ Map<StringBuilderNode, Set<StringBuilderNode>> materializing = new IdentityHashMap<>();
+ Set<StringBuilderNode> escaping = Sets.newIdentityHashSet();
+
+ Map<StringBuilderNode, StringBuilderNode> nodeToRoots = new IdentityHashMap<>();
+ Map<StringBuilderNode, Set<StringBuilderNode>> stringBuilderDependencies =
+ new IdentityHashMap<>();
+
+ stringBuilderGraphs.forEach(
+ (value, root) -> {
+ WorkList<StringBuilderNode> workList = WorkList.newIdentityWorkList(root);
+ Set<StringBuilderNode> materializingInstructions = Sets.newIdentityHashSet();
+ materializing.put(root, materializingInstructions);
+ while (workList.hasNext()) {
+ StringBuilderNode next = workList.next();
+ nodeToRoots.put(next, root);
+ if (next.isInitOrAppend()) {
+ ImplicitToStringNode dependency = next.asInitOrAppend().getImplicitToStringNode();
+ if (dependency != null) {
+ stringBuilderDependencies
+ .computeIfAbsent(root, ignoreArgument(Sets::newIdentityHashSet))
+ .add(dependency);
+ }
+ }
+ if (next.isLoopNode()) {
+ looping.add(root);
+ }
+ if (next.isEscapeNode()) {
+ inspectingCapacity.add(root);
+ escaping.add(root);
+ }
+ if (next.isToStringNode() || next.isImplicitToStringNode()) {
+ materializingInstructions.add(root);
+ }
+ if (next.isInspectingNode()) {
+ inspectingCapacity.add(root);
+ }
+ next.getSuccessors().forEach(workList::addFirstIfNotSeen);
+ }
+ });
+
+ MunchingState munchingState =
+ new MunchingState(actions, escaping, inspectingCapacity, looping, materializing, oracle);
+
+ boolean keepMunching = true;
+ for (int i = 0; i < NUMBER_OF_MUNCHING_PASSES && keepMunching; i++) {
+ keepMunching = false;
+ for (StringBuilderNode root :
+ computeProcessingOrder(stringBuilderGraphs, stringBuilderDependencies, nodeToRoots)) {
+ WorkList<StringBuilderNode> workList = WorkList.newIdentityWorkList(root);
+ while (workList.hasNext()) {
+ StringBuilderNode next = workList.next();
+ keepMunching |= StringBuilderNodeMuncher.optimize(root, next, munchingState);
+ next.getSuccessors().forEach(workList::addFirstIfNotSeen);
+ }
+ }
+ }
+ return actions;
+ }
+
+ private Collection<StringBuilderNode> computeProcessingOrder(
+ Map<Value, StringBuilderNode> stringBuilderGraphs,
+ Map<StringBuilderNode, Set<StringBuilderNode>> stringBuilderDependencies,
+ Map<StringBuilderNode, StringBuilderNode> nodeToRoots) {
+ // Make a topological sort to ensure we visit all nodes in the best order for optimizing nested
+ // string builders.
+ Set<StringBuilderNode> processingOrder = new LinkedHashSet<>();
+ new DepthFirstSearchWorkList<StringBuilderNode, Void, Void>() {
+
+ @Override
+ @SuppressWarnings("ReturnValueIgnored")
+ protected TraversalContinuation<Void, Void> process(
+ DFSNode<StringBuilderNode> node,
+ Function<StringBuilderNode, DFSNode<StringBuilderNode>> childNodeConsumer) {
+ StringBuilderNode root = node.getNode();
+ Set<StringBuilderNode> stringBuilderNodes = stringBuilderDependencies.get(root);
+ if (stringBuilderNodes != null) {
+ for (StringBuilderNode dependency : stringBuilderNodes) {
+ childNodeConsumer.apply(nodeToRoots.get(dependency));
+ }
+ }
+ return TraversalContinuation.doContinue();
+ }
+
+ @Override
+ protected List<Void> getFinalStateForRoots(Collection<StringBuilderNode> roots) {
+ return null;
+ }
+
+ @Override
+ public TraversalContinuation<Void, Void> joiner(DFSNode<StringBuilderNode> node) {
+ StringBuilderNode node1 = node.getNode();
+ processingOrder.add(node1);
+ return TraversalContinuation.doContinue();
+ }
+ }.run(stringBuilderGraphs.values());
+ return processingOrder;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java
new file mode 100644
index 0000000..f1e7fb8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java
@@ -0,0 +1,279 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.MapUtils;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public class StringBuilderEscapeState extends AbstractState<StringBuilderEscapeState> {
+
+ private static final StringBuilderEscapeState BOTTOM = new StringBuilderEscapeState();
+
+ public static StringBuilderEscapeState bottom() {
+ return BOTTOM;
+ }
+
+ private final Map<Value, Set<Value>> aliasesToDefinitions;
+ private final Map<Value, Set<Value>> definitionsToAliases;
+ private final Set<Value> escaping;
+ private final Set<Value> liveStringBuilders;
+
+ // Special set for finding new escaped string builders to save us from computing the delta between
+ // two states. The set is not part of the fixed-point computation (and is not checked for equals).
+ private final Set<Value> newlyEscaped;
+
+ private StringBuilderEscapeState() {
+ aliasesToDefinitions = Collections.emptyMap();
+ definitionsToAliases = Collections.emptyMap();
+ escaping = Collections.emptySet();
+ liveStringBuilders = Collections.emptySet();
+ newlyEscaped = Collections.emptySet();
+ }
+
+ public StringBuilderEscapeState(
+ Map<Value, Set<Value>> aliasesToDefinitions,
+ Map<Value, Set<Value>> definitionsToAliases,
+ Set<Value> escaping,
+ Set<Value> liveStringBuilders,
+ Set<Value> newlyEscaped) {
+ assert !aliasesToDefinitions.isEmpty()
+ || !escaping.isEmpty()
+ || !definitionsToAliases.isEmpty()
+ || !liveStringBuilders.isEmpty()
+ : "Creating an instance of BOTTOM";
+ this.aliasesToDefinitions = aliasesToDefinitions;
+ this.definitionsToAliases = definitionsToAliases;
+ this.escaping = escaping;
+ this.liveStringBuilders = liveStringBuilders;
+ this.newlyEscaped = newlyEscaped;
+ }
+
+ public Set<Value> getEscaping() {
+ return escaping;
+ }
+
+ public Map<Value, Set<Value>> getAliasesToDefinitions() {
+ return aliasesToDefinitions;
+ }
+
+ public Map<Value, Set<Value>> getDefinitionsToAliases() {
+ return definitionsToAliases;
+ }
+
+ public Set<Value> getLiveStringBuilders() {
+ return liveStringBuilders;
+ }
+
+ public boolean isLiveStringBuilder(Value sb) {
+ return liveStringBuilders.contains(sb);
+ }
+
+ public boolean isEscaped(Value sb) {
+ return escaping.contains(sb);
+ }
+
+ /**
+ * Should only be used when iterating a completed fix point computation and stepping through
+ * instructions applying the transfer function directly.
+ */
+ public Set<Value> getNewlyEscaped() {
+ return newlyEscaped;
+ }
+
+ public boolean isBottom() {
+ return this == BOTTOM;
+ }
+
+ @Override
+ public StringBuilderEscapeState join(AppView<?> appView, StringBuilderEscapeState other) {
+ if (this.isBottom()) {
+ return other;
+ } else if (other.isBottom()) {
+ return this;
+ } else {
+ Builder builder =
+ builder().addEscaping(other.escaping).addLiveStringBuilders(other.liveStringBuilders);
+ other.aliasesToDefinitions.forEach(builder::addAliasesToDefinitions);
+ other.definitionsToAliases.forEach(builder::addDefinitionsToAliases);
+ return builder.build();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof StringBuilderEscapeState)) {
+ return false;
+ }
+ StringBuilderEscapeState that = (StringBuilderEscapeState) o;
+ return MapUtils.equals(aliasesToDefinitions, that.aliasesToDefinitions)
+ && MapUtils.equals(definitionsToAliases, that.definitionsToAliases)
+ && escaping.equals(that.escaping)
+ && liveStringBuilders.equals(that.liveStringBuilders);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(aliasesToDefinitions, definitionsToAliases, escaping, liveStringBuilders);
+ }
+
+ @Override
+ public StringBuilderEscapeState asAbstractState() {
+ return this;
+ }
+
+ public Builder builder() {
+ return new Builder(this);
+ }
+
+ public static class Builder {
+
+ private Map<Value, Set<Value>> aliasesToDefinitions;
+ private Map<Value, Set<Value>> definitionsToAliases;
+ private Set<Value> escaped;
+ private Set<Value> liveStringBuilders;
+ private final Set<Value> newlyEscaping = new HashSet<>();
+
+ private final StringBuilderEscapeState previous;
+
+ public Builder(StringBuilderEscapeState previous) {
+ aliasesToDefinitions = previous.aliasesToDefinitions;
+ definitionsToAliases = previous.definitionsToAliases;
+ escaped = previous.escaping;
+ liveStringBuilders = previous.liveStringBuilders;
+ this.previous = previous;
+ }
+
+ public Builder addAliasesToDefinitions(Value key, Set<Value> stringBuilders) {
+ ensureAliasesToDefinitions();
+ aliasesToDefinitions
+ .computeIfAbsent(key, ignoreArgument(HashSet::new))
+ .addAll(stringBuilders);
+ return this;
+ }
+
+ public Builder addDefinitionsToAliases(Value key, Set<Value> stringBuilders) {
+ ensureDefinitionToAliases();
+ definitionsToAliases
+ .computeIfAbsent(key, ignoreArgument(HashSet::new))
+ .addAll(stringBuilders);
+ return this;
+ }
+
+ public Builder addAlias(Value key, Value stringBuilder) {
+ ensureAliasesToDefinitions();
+ ensureDefinitionToAliases();
+ aliasesToDefinitions.computeIfAbsent(key, ignoreArgument(HashSet::new)).add(stringBuilder);
+ definitionsToAliases.computeIfAbsent(stringBuilder, ignoreArgument(HashSet::new)).add(key);
+ return this;
+ }
+
+ public Builder addEscaping(Collection<Value> escaping) {
+ if (escaping == null) {
+ return this;
+ }
+ ensureNewEscaping();
+ this.escaped.addAll(escaping);
+ return this;
+ }
+
+ public Builder addEscaping(Value value) {
+ ensureNewEscaping();
+ if (escaped.add(value)) {
+ newlyEscaping.add(value);
+ }
+ return this;
+ }
+
+ public Builder addLiveStringBuilders(Collection<Value> liveStringBuilders) {
+ ensureNewLiveStringBuilders();
+ this.liveStringBuilders.addAll(liveStringBuilders);
+ return this;
+ }
+
+ public Builder addLiveStringBuilder(Value stringBuilder) {
+ ensureNewLiveStringBuilders();
+ liveStringBuilders.add(stringBuilder);
+ return this;
+ }
+
+ private void ensureAliasesToDefinitions() {
+ if (aliasesToDefinitions == previous.aliasesToDefinitions) {
+ aliasesToDefinitions = new HashMap<>(previous.aliasesToDefinitions.size() + 1);
+ previous.aliasesToDefinitions.forEach(
+ (key, value) -> aliasesToDefinitions.put(key, Sets.newHashSet(value)));
+ }
+ }
+
+ private void ensureDefinitionToAliases() {
+ if (definitionsToAliases == previous.definitionsToAliases) {
+ definitionsToAliases = new HashMap<>(previous.definitionsToAliases.size() + 1);
+ previous.definitionsToAliases.forEach(
+ (key, value) -> definitionsToAliases.put(key, Sets.newHashSet(value)));
+ }
+ }
+
+ private void ensureNewEscaping() {
+ if (escaped == previous.escaping) {
+ escaped = new HashSet<>(escaped);
+ }
+ }
+
+ private void ensureNewLiveStringBuilders() {
+ if (liveStringBuilders == previous.liveStringBuilders) {
+ liveStringBuilders = new HashSet<>(liveStringBuilders);
+ }
+ }
+
+ public Set<Value> getLiveStringBuilders() {
+ return liveStringBuilders;
+ }
+
+ public Map<Value, Set<Value>> getAliasesToDefinitions() {
+ return aliasesToDefinitions;
+ }
+
+ public Map<Value, Set<Value>> getDefinitionsToAliases() {
+ return definitionsToAliases;
+ }
+
+ public StringBuilderEscapeState build() {
+ assert liveStringBuilders.containsAll(escaped)
+ : "Escaping is not a subset of live string builders";
+ assert liveStringBuilders.containsAll(aliasesToDefinitions.keySet())
+ : "Aliases is not a subset of live string builders";
+ assert escaped.containsAll(newlyEscaping)
+ : "Unexpected value in newlyEscaping not in escaping";
+ assert liveStringBuilders.containsAll(definitionsToAliases.keySet())
+ : "Escaped definitions should all be live";
+ assert definitionsToAliases.values().stream()
+ .allMatch(phis -> liveStringBuilders.containsAll(phis))
+ : "All known escaping definitions should be live string builders";
+ if (previous.liveStringBuilders == liveStringBuilders
+ && previous.escaping == escaped
+ && previous.aliasesToDefinitions == aliasesToDefinitions
+ && previous.definitionsToAliases == definitionsToAliases) {
+ previous.getNewlyEscaped().clear();
+ return previous;
+ }
+ return new StringBuilderEscapeState(
+ aliasesToDefinitions, definitionsToAliases, escaped, liveStringBuilders, newlyEscaping);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeTransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeTransferFunction.java
new file mode 100644
index 0000000..ff4a62a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeTransferFunction.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.ir.optimize.string.StringBuilderHelper.isEscapingInstructionForInValues;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderHelper.isEscapingInstructionForOutValues;
+import static com.android.tools.r8.ir.optimize.string.StringBuilderHelper.isInstructionThatIntroducesDefiniteAlias;
+
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+/**
+ * The StringBuilderEscapeTransferFunction will compute all escaping string builders at any point in
+ * the program. It does so by maintaining a state keeping track of all values known to be string
+ * builders (phi's, assumes, checkcasts) and track when a value escapes.
+ */
+public class StringBuilderEscapeTransferFunction
+ implements AbstractTransferFunction<BasicBlock, Instruction, StringBuilderEscapeState> {
+
+ private final StringBuilderOracle oracle;
+
+ public StringBuilderEscapeTransferFunction(StringBuilderOracle oracle) {
+ this.oracle = oracle;
+ }
+
+ @Override
+ public TransferFunctionResult<StringBuilderEscapeState> applyBlock(
+ BasicBlock block, StringBuilderEscapeState state) {
+ StringBuilderEscapeState.Builder builder = state.builder();
+ block
+ .getPhis()
+ .forEach(
+ phi -> {
+ if (oracle.hasStringBuilderType(phi)) {
+ builder.addLiveStringBuilder(phi);
+ }
+ for (Value operand : phi.getOperands()) {
+ if (isLiveStringBuilder(builder, operand)) {
+ builder.addLiveStringBuilder(phi);
+ builder.addAlias(phi, operand);
+ }
+ }
+ });
+ return builder.build();
+ }
+
+ @Override
+ public TransferFunctionResult<StringBuilderEscapeState> apply(
+ Instruction instruction, StringBuilderEscapeState state) {
+ StringBuilderEscapeState.Builder builder = state.builder();
+ boolean isStringBuilderInstruction = oracle.isModeledStringBuilderInstruction(instruction);
+ if (!isStringBuilderInstruction && isEscapingInstructionForInValues(instruction)) {
+ for (Value inValue : instruction.inValues()) {
+ if (isLiveStringBuilder(builder, inValue)) {
+ // TODO(b/232377424): Account for calls to Object (such as Object.toString()).
+ builder.addEscaping(inValue);
+ }
+ }
+ }
+ assert !isStringBuilderInstruction
+ || builder.getLiveStringBuilders().contains(instruction.getFirstOperand());
+ Value outValue = instruction.outValue();
+ if (outValue != null) {
+ if (isInstructionThatIntroducesDefiniteAlias(instruction, oracle)
+ && isLiveStringBuilder(builder, instruction.getFirstOperand())) {
+ builder.addLiveStringBuilder(outValue);
+ builder.addAlias(outValue, instruction.getFirstOperand());
+ } else if (oracle.hasStringBuilderType(outValue)) {
+ builder.addLiveStringBuilder(outValue);
+ }
+ if (!isStringBuilderInstruction
+ && isLiveStringBuilder(builder, instruction.outValue())
+ && (isEscapingInstructionForOutValues(instruction))) {
+ builder.addEscaping(instruction.outValue());
+ }
+ }
+ return builder.build();
+ }
+
+ private boolean isLiveStringBuilder(StringBuilderEscapeState.Builder builderState, Value value) {
+ return builderState.getLiveStringBuilders().contains(value);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderHelper.java
new file mode 100644
index 0000000..a795308
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderHelper.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import 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.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.NumberConversion;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
+
+public class StringBuilderHelper {
+
+ static boolean isEscapingInstructionForInValues(Instruction instruction) {
+ return instruction.isFieldPut()
+ || instruction.isInvoke()
+ || instruction.isReturn()
+ || instruction.isArrayPut();
+ }
+
+ static boolean isEscapingInstructionForOutValues(Instruction instruction) {
+ return instruction.isArgument()
+ || instruction.isInvoke()
+ || instruction.isFieldGet()
+ || instruction.isArrayGet()
+ || instruction.isCheckCast();
+ }
+
+ static boolean canMutate(Instruction instruction) {
+ return instruction.isInvoke()
+ || instruction.isFieldInstruction()
+ || instruction.isNewInstance();
+ }
+
+ static boolean isInstructionThatIntroducesDefiniteAlias(
+ Instruction instruction, StringBuilderOracle oracle) {
+ return instruction.isAssume() || instruction.isCheckCast() || oracle.isAppend(instruction);
+ }
+
+ static String extractConstantArgument(
+ DexItemFactory factory, DexMethod method, Value arg, DexType argumentType) {
+ if (arg.isPhi()) {
+ return null;
+ }
+ if (arg.isConstString()) {
+ return arg.definition.asConstString().getValue().toString();
+ }
+ Number constantNumber = extractConstantNumber(factory, arg);
+ if (constantNumber == null) {
+ return null;
+ }
+ if (arg.getType().isPrimitiveType()) {
+ if (argumentType == factory.booleanType) {
+ return String.valueOf(constantNumber.intValue() != 0);
+ } else if (argumentType == factory.byteType) {
+ return String.valueOf(constantNumber.byteValue());
+ } else if (argumentType == factory.shortType) {
+ return String.valueOf(constantNumber.shortValue());
+ } else if (argumentType == factory.charType) {
+ return String.valueOf((char) constantNumber.intValue());
+ } else if (argumentType == factory.intType) {
+ return String.valueOf(constantNumber.intValue());
+ } else if (argumentType == factory.longType) {
+ return String.valueOf(constantNumber.longValue());
+ } else if (argumentType == factory.floatType) {
+ return String.valueOf(constantNumber.floatValue());
+ } else if (argumentType == factory.doubleType) {
+ return String.valueOf(constantNumber.doubleValue());
+ }
+ } else if (arg.getType().isNullType()
+ && !method.isInstanceInitializer(factory)
+ && argumentType != factory.charArrayType) {
+ assert constantNumber.intValue() == 0;
+ return "null";
+ }
+ return null;
+ }
+
+ static Number extractConstantNumber(DexItemFactory factory, Value arg) {
+ if (arg.isPhi()) {
+ return null;
+ }
+ if (arg.definition.isConstNumber()) {
+ ConstNumber cst = arg.definition.asConstNumber();
+ if (cst.outType() == ValueType.LONG) {
+ return cst.getLongValue();
+ } else if (cst.outType() == ValueType.FLOAT) {
+ return cst.getFloatValue();
+ } else if (cst.outType() == ValueType.DOUBLE) {
+ return cst.getDoubleValue();
+ } else {
+ assert cst.outType() == ValueType.INT || cst.outType() == ValueType.OBJECT;
+ return cst.getIntValue();
+ }
+ } else if (arg.definition.isNumberConversion()) {
+ NumberConversion conversion = arg.definition.asNumberConversion();
+ assert conversion.inValues().size() == 1;
+ Number temp = extractConstantNumber(factory, conversion.inValues().get(0));
+ if (temp == null) {
+ return null;
+ }
+ DexType conversionType = conversion.to.toDexType(factory);
+ if (conversionType == factory.booleanType) {
+ return temp.intValue() != 0 ? 1 : 0;
+ } else if (conversionType == factory.byteType) {
+ return temp.byteValue();
+ } else if (conversionType == factory.shortType) {
+ return temp.shortValue();
+ } else if (conversionType == factory.charType) {
+ return temp.intValue();
+ } else if (conversionType == factory.intType) {
+ return temp.intValue();
+ } else if (conversionType == factory.longType) {
+ return temp.longValue();
+ } else if (conversionType == factory.floatType) {
+ return temp.floatValue();
+ } else if (conversionType == factory.doubleType) {
+ return temp.doubleValue();
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
new file mode 100644
index 0000000..acd5c47
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
@@ -0,0 +1,606 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * StringBuilderNode defines a single point where a string builder operation occur or some abstract
+ * state changes. The node can be assembled into a graph by defining predecessors and successors.
+ */
+class StringBuilderNode {
+
+ interface StringBuilderInstruction {
+
+ Instruction getInstruction();
+
+ boolean isStringBuilderInstructionNode();
+
+ StringBuilderInstruction asStringBuilderInstructionNode();
+ }
+
+ interface InitOrAppend extends StringBuilderInstruction {
+
+ boolean hasConstantArgument();
+
+ String getConstantArgument();
+
+ void setConstantArgument(String constantArgument);
+
+ void setImplicitToStringNode(ImplicitToStringNode node);
+
+ ImplicitToStringNode getImplicitToStringNode();
+ }
+
+ private final Set<StringBuilderNode> successors = Sets.newIdentityHashSet();
+ private final Set<StringBuilderNode> predecessors = Sets.newIdentityHashSet();
+
+ // Field uses to ensure that munching will not operate on the same value multiple times. If all
+ // peep holes would look in the same direction, this field could be removed.
+ private boolean isDead = false;
+
+ private StringBuilderNode() {}
+
+ boolean isEscapeNode() {
+ return false;
+ }
+
+ boolean isMutateNode() {
+ return false;
+ }
+
+ boolean isSplitReferenceNode() {
+ return false;
+ }
+
+ boolean isLoopNode() {
+ return false;
+ }
+
+ boolean isNewInstanceNode() {
+ return false;
+ }
+
+ boolean isInitNode() {
+ return false;
+ }
+
+ boolean isAppendNode() {
+ return false;
+ }
+
+ boolean isInitOrAppend() {
+ return false;
+ }
+
+ boolean isToStringNode() {
+ return false;
+ }
+
+ boolean isInspectingNode() {
+ return false;
+ }
+
+ boolean isOtherStringBuilderNode() {
+ return false;
+ }
+
+ boolean isImplicitToStringNode() {
+ return false;
+ }
+
+ boolean isStringBuilderInstructionNode() {
+ return false;
+ }
+
+ NewInstanceNode asNewInstanceNode() {
+ return null;
+ }
+
+ InitNode asInitNode() {
+ return null;
+ }
+
+ AppendNode asAppendNode() {
+ return null;
+ }
+
+ InitOrAppend asInitOrAppend() {
+ return null;
+ }
+
+ ToStringNode asToStringNode() {
+ return null;
+ }
+
+ InspectingNode asInspectingNode() {
+ return null;
+ }
+
+ OtherStringBuilderNode asOtherStringBuilderNode() {
+ return null;
+ }
+
+ ImplicitToStringNode asImplicitToStringNode() {
+ return null;
+ }
+
+ StringBuilderInstruction asStringBuilderInstructionNode() {
+ return null;
+ }
+
+ boolean isDead() {
+ return isDead;
+ }
+
+ boolean hasSingleSuccessor() {
+ return successors.size() == 1;
+ }
+
+ StringBuilderNode getSingleSuccessor() {
+ assert hasSingleSuccessor();
+ return successors.iterator().next();
+ }
+
+ void addSuccessor(StringBuilderNode successor) {
+ successors.add(successor);
+ successor.predecessors.add(this);
+ }
+
+ Set<StringBuilderNode> getSuccessors() {
+ return successors;
+ }
+
+ boolean hasSinglePredecessor() {
+ return predecessors.size() == 1;
+ }
+
+ StringBuilderNode getSinglePredecessor() {
+ assert hasSinglePredecessor();
+ return predecessors.iterator().next();
+ }
+
+ Set<StringBuilderNode> getPredecessors() {
+ return predecessors;
+ }
+
+ void addPredecessor(StringBuilderNode predecessor) {
+ predecessors.add(predecessor);
+ }
+
+ void removeNode() {
+ for (StringBuilderNode successor : this.getSuccessors()) {
+ successor.getPredecessors().remove(this);
+ successor.getPredecessors().addAll(this.getPredecessors());
+ }
+ for (StringBuilderNode predecessor : this.getPredecessors()) {
+ predecessor.getSuccessors().remove(this);
+ predecessor.getSuccessors().addAll(this.getSuccessors());
+ }
+ isDead = true;
+ }
+
+ /** EscapeNode is used to explicitly define an escape point for a StringBuilder. */
+ static class EscapeNode extends StringBuilderNode {
+
+ @Override
+ boolean isEscapeNode() {
+ return true;
+ }
+ }
+
+ /**
+ * MutateNode defines if a string builder could be possibly mutated or inspected. An example could
+ * be:
+ *
+ * <pre>
+ * sb = new StringBuilder();
+ * escape(sb);
+ * sb.append("foo");
+ * canMutate(); <-- This is represented by a MutateNode.
+ * sb.append("bar");
+ * </pre>
+ */
+ static class MutateNode extends StringBuilderNode {
+
+ @Override
+ boolean isMutateNode() {
+ return true;
+ }
+ }
+
+ /**
+ * SplitReferenceNodes are synthetic nodes inserted when a string builder is used in multiple
+ * successor blocks.
+ */
+ static class SplitReferenceNode extends StringBuilderNode {
+
+ @Override
+ boolean isSplitReferenceNode() {
+ return true;
+ }
+ }
+
+ /**
+ * LoopNode is a node indicating that all successor paths are part of a loop. LoopNode's are only
+ * inserted once ensuring there are no loops in a StringBuilderGraph.
+ */
+ static class LoopNode extends StringBuilderNode {
+
+ @Override
+ boolean isLoopNode() {
+ return true;
+ }
+ }
+
+ /** A NewInstanceNode is a new instance of either StringBuilder or StringBuffer. */
+ static class NewInstanceNode extends StringBuilderNode implements StringBuilderInstruction {
+
+ private final NewInstance instruction;
+
+ private NewInstanceNode(NewInstance instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isNewInstanceNode() {
+ return true;
+ }
+
+ @Override
+ NewInstanceNode asNewInstanceNode() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+ }
+
+ /** An initNode is where a StringBuilder/StringBuffer is initialized. */
+ static class InitNode extends StringBuilderNode
+ implements InitOrAppend, StringBuilderInstruction {
+
+ private final InvokeDirect instruction;
+ private ImplicitToStringNode implicitToStringNode;
+ private String constantArgument;
+
+ private InitNode(InvokeDirect instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isInitNode() {
+ return true;
+ }
+
+ @Override
+ boolean isInitOrAppend() {
+ return true;
+ }
+
+ @Override
+ InitNode asInitNode() {
+ return this;
+ }
+
+ @Override
+ InitOrAppend asInitOrAppend() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+
+ @Override
+ public void setConstantArgument(String constantArgument) {
+ this.constantArgument = constantArgument;
+ }
+
+ @Override
+ public void setImplicitToStringNode(ImplicitToStringNode node) {
+ implicitToStringNode = node;
+ }
+
+ @Override
+ public ImplicitToStringNode getImplicitToStringNode() {
+ return implicitToStringNode;
+ }
+
+ @Override
+ public String getConstantArgument() {
+ return constantArgument;
+ }
+
+ @Override
+ public boolean hasConstantArgument() {
+ return constantArgument != null;
+ }
+ }
+
+ /** AppendNodes are StringBuilder.append or StringBuffer.append. */
+ static class AppendNode extends StringBuilderNode
+ implements InitOrAppend, StringBuilderInstruction {
+
+ private final InvokeVirtual instruction;
+ private ImplicitToStringNode implicitToStringNode;
+ private String constantArgument;
+
+ private AppendNode(InvokeVirtual instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isAppendNode() {
+ return true;
+ }
+
+ @Override
+ boolean isInitOrAppend() {
+ return true;
+ }
+
+ @Override
+ AppendNode asAppendNode() {
+ return this;
+ }
+
+ @Override
+ InitOrAppend asInitOrAppend() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+
+ @Override
+ public void setConstantArgument(String constantArgument) {
+ this.constantArgument = constantArgument;
+ }
+
+ @Override
+ public void setImplicitToStringNode(ImplicitToStringNode node) {
+ implicitToStringNode = node;
+ }
+
+ @Override
+ public ImplicitToStringNode getImplicitToStringNode() {
+ return implicitToStringNode;
+ }
+
+ @Override
+ public String getConstantArgument() {
+ return constantArgument;
+ }
+
+ @Override
+ public boolean hasConstantArgument() {
+ return constantArgument != null;
+ }
+ }
+
+ /**
+ * ToStringNodes marked a point where a StringBuilder/StringBuffer is materialized to a string.
+ */
+ static class ToStringNode extends StringBuilderNode implements StringBuilderInstruction {
+
+ private final InvokeVirtual instruction;
+
+ private ToStringNode(InvokeVirtual instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isToStringNode() {
+ return true;
+ }
+
+ @Override
+ ToStringNode asToStringNode() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+ }
+
+ /**
+ * InspectingNode is inserted if there is inspection of the capacity of a
+ * StringBuilder/StringBuffer. Special care needs to be taken since we try to ensure that capacity
+ * will be the same after optimizations.
+ */
+ static class InspectingNode extends StringBuilderNode implements StringBuilderInstruction {
+
+ private final Instruction instruction;
+
+ private InspectingNode(Instruction instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isInspectingNode() {
+ return true;
+ }
+
+ @Override
+ InspectingNode asInspectingNode() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+ }
+
+ /** OtherStringBuilderNode marks operations on string builders we do not model. */
+ static class OtherStringBuilderNode extends StringBuilderNode
+ implements StringBuilderInstruction {
+
+ private final Instruction instruction;
+
+ private OtherStringBuilderNode(Instruction instruction) {
+ this.instruction = instruction;
+ }
+
+ @Override
+ boolean isOtherStringBuilderNode() {
+ return true;
+ }
+
+ @Override
+ OtherStringBuilderNode asOtherStringBuilderNode() {
+ return this;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ @Override
+ public boolean isStringBuilderInstructionNode() {
+ return true;
+ }
+
+ @Override
+ public StringBuilderInstruction asStringBuilderInstructionNode() {
+ return this;
+ }
+ }
+
+ /**
+ * ImplicitToStringNode are placed a StringBuilder/StringBuffer is appended to another
+ * StringBuilder/StringBuffer.
+ */
+ static class ImplicitToStringNode extends StringBuilderNode {
+
+ private final InitOrAppend initOrAppend;
+
+ ImplicitToStringNode(InitOrAppend initOrAppend) {
+ this.initOrAppend = initOrAppend;
+ }
+
+ public InitOrAppend getInitOrAppend() {
+ return initOrAppend;
+ }
+
+ @Override
+ boolean isImplicitToStringNode() {
+ return true;
+ }
+
+ @Override
+ ImplicitToStringNode asImplicitToStringNode() {
+ return this;
+ }
+ }
+
+ static EscapeNode createEscapeNode() {
+ return new EscapeNode();
+ }
+
+ static MutateNode createMutateNode() {
+ return new MutateNode();
+ }
+
+ static SplitReferenceNode createSplitReferenceNode() {
+ return new SplitReferenceNode();
+ }
+
+ static LoopNode createLoopNode() {
+ return new LoopNode();
+ }
+
+ static NewInstanceNode createNewInstanceNode(NewInstance instruction) {
+ return new NewInstanceNode(instruction);
+ }
+
+ static InitNode createInitNode(InvokeDirect instruction) {
+ return new InitNode(instruction);
+ }
+
+ static AppendNode createAppendNode(InvokeVirtual instruction) {
+ return new AppendNode(instruction);
+ }
+
+ static ToStringNode createToStringNode(InvokeVirtual instruction) {
+ return new ToStringNode(instruction);
+ }
+
+ static InspectingNode createInspectionNode(Instruction instruction) {
+ return new InspectingNode(instruction);
+ }
+
+ static OtherStringBuilderNode createOtherStringBuilderNode(Instruction instruction) {
+ return new OtherStringBuilderNode(instruction);
+ }
+
+ static ImplicitToStringNode createImplicitToStringNode(InitOrAppend otherNode) {
+ return new ImplicitToStringNode(otherNode);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
new file mode 100644
index 0000000..000ca52
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
@@ -0,0 +1,340 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.AppendWithNewConstantString;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.RemoveStringBuilderAction;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByConstantString;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.StringBuilderInstruction;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ToStringNode;
+import com.android.tools.r8.utils.WorkList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * StringBuilderNodeMuncher is a classic munching algorithm that will try to remove nodes on string
+ * builders by looking at small view of it {@link PeepholePattern}. If a pattern can be optimized
+ * the graph will be updated in place and if the IR needs to be updated, an action will be added.
+ */
+class StringBuilderNodeMuncher {
+
+ static class MunchingState {
+
+ private final Map<Instruction, StringBuilderAction> actions;
+ private final StringBuilderOracle oracle;
+
+ // The below state can all be computed by searching the graph but are tracked here to improve
+ // performance.
+ private final Set<StringBuilderNode> escaping;
+ private final Set<StringBuilderNode> inspectingCapacity;
+ private final Set<StringBuilderNode> looping;
+ private final Map<StringBuilderNode, Set<StringBuilderNode>> materializingInstructions;
+ private final Map<Value, String> optimizedStrings = new IdentityHashMap<>();
+
+ MunchingState(
+ Map<Instruction, StringBuilderAction> actions,
+ Set<StringBuilderNode> escaping,
+ Set<StringBuilderNode> inspectingCapacity,
+ Set<StringBuilderNode> looping,
+ Map<StringBuilderNode, Set<StringBuilderNode>> materializingInstructions,
+ StringBuilderOracle oracle) {
+ this.actions = actions;
+ this.escaping = escaping;
+ this.inspectingCapacity = inspectingCapacity;
+ this.looping = looping;
+ this.materializingInstructions = materializingInstructions;
+ this.oracle = oracle;
+ }
+ }
+
+ private interface PeepholePattern {
+
+ boolean optimize(
+ StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState);
+ }
+
+ /**
+ * This peephole will try to optimize two sequential appends:
+ *
+ * <pre>
+ * append("foo") -> append("bar") =>
+ * append("foobar")
+ *
+ * or
+ *
+ * init("foo") -> append("bar") =>
+ * init("foobar")
+ * </pre>
+ */
+ private static class MunchAppends implements PeepholePattern {
+
+ @Override
+ public boolean optimize(
+ StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
+ if (!currentNode.isAppendNode()) {
+ return false;
+ }
+ String currentConstantArgument = getConstantArgumentForNode(currentNode, munchingState);
+ if (currentConstantArgument == null || !currentNode.hasSinglePredecessor()) {
+ return false;
+ }
+ StringBuilderNode previous = currentNode.getSinglePredecessor();
+ String previousConstantArgument = getConstantArgumentForNode(previous, munchingState);
+ if (previousConstantArgument == null || !previous.hasSingleSuccessor()) {
+ return false;
+ }
+ // The capacity changes based on the init call (on JVM it adds 16 to length of input).
+ if (previous.isInitNode() && munchingState.inspectingCapacity.contains(root)) {
+ return false;
+ }
+ assert previous.isInitOrAppend();
+ String newConstant = previousConstantArgument + currentConstantArgument;
+ InitOrAppend initOrAppend = previous.asInitOrAppend();
+ initOrAppend.setConstantArgument(newConstant);
+ munchingState.actions.put(
+ initOrAppend.getInstruction(), new AppendWithNewConstantString(newConstant));
+ munchingState.actions.put(
+ currentNode.asAppendNode().getInstruction(), RemoveStringBuilderAction.getInstance());
+ currentNode.removeNode();
+ return true;
+ }
+ }
+
+ /**
+ * This peephole will try to remove toString nodes and replace by a constant string:
+ *
+ * <pre>
+ * newInstance -> init("foo") -> append("bar") -> toString() =>
+ * newInstance -> init("foo") -> append("bar")
+ * </pre>
+ *
+ * <p>If the node is an implicitToString, we update the append of another builder to have the new
+ * constant value directly. If not, we keep track of the outValue toString() had is replaced by a
+ * constant, by updating {@code MunchingState.optimizedStrings}
+ */
+ private static class MunchToString implements PeepholePattern {
+
+ @Override
+ public boolean optimize(
+ StringBuilderNode originalRoot,
+ StringBuilderNode currentNode,
+ MunchingState munchingState) {
+ if (!currentNode.isToStringNode() && !currentNode.isImplicitToStringNode()) {
+ return false;
+ }
+ StringBuilderNode root = findFirstNonSentinelRoot(originalRoot);
+ if (!root.isNewInstanceNode() || !root.hasSingleSuccessor()) {
+ return false;
+ }
+ StringBuilderNode init = root.getSingleSuccessor();
+ String rootConstantArgument = getConstantArgumentForNode(init, munchingState);
+ if (rootConstantArgument == null || !init.isInitNode()) {
+ return false;
+ }
+ // This is either <init>(str) -> toString() or <init>(str) -> append(str) -> toString()
+ // If the string builder dependency is directly given to another string builder, there is
+ // no toString() but an append with this string builder as argument.
+ if (!currentNode.hasSinglePredecessor() || !init.hasSingleSuccessor()) {
+ return false;
+ }
+ String constantArgument = null;
+ if (currentNode.getSinglePredecessor() == init) {
+ constantArgument = rootConstantArgument;
+ } else {
+ StringBuilderNode expectedAppend = init.getSingleSuccessor();
+ StringBuilderNode expectedSameAppend = currentNode.getSinglePredecessor();
+ String appendConstantArgument = getConstantArgumentForNode(expectedAppend, munchingState);
+ if (expectedAppend == expectedSameAppend && appendConstantArgument != null) {
+ // TODO(b/190489514): See if this larger pattern is necessary.
+ assert false : "See why this larger pattern is necessary";
+ constantArgument = rootConstantArgument + appendConstantArgument;
+ }
+ }
+ if (constantArgument == null) {
+ return false;
+ }
+ if (currentNode.isToStringNode()) {
+ ToStringNode toStringNode = currentNode.asToStringNode();
+ munchingState.actions.put(
+ toStringNode.getInstruction(), new ReplaceByConstantString(constantArgument));
+ munchingState.materializingInstructions.get(originalRoot).remove(currentNode);
+ String oldValue =
+ munchingState.optimizedStrings.put(
+ toStringNode.getInstruction().outValue(), constantArgument);
+ assert oldValue == null;
+ } else {
+ assert currentNode.isImplicitToStringNode();
+ ImplicitToStringNode implicitToStringNode = currentNode.asImplicitToStringNode();
+ InitOrAppend initOrAppend = implicitToStringNode.getInitOrAppend();
+ initOrAppend.setConstantArgument(constantArgument);
+ munchingState.actions.put(
+ initOrAppend.getInstruction(), new AppendWithNewConstantString(constantArgument));
+ }
+ currentNode.removeNode();
+ return true;
+ }
+ }
+
+ /**
+ * Find the first non split reference node or loop-node, which are nodes inserted to track
+ * control-flow.
+ */
+ private static StringBuilderNode findFirstNonSentinelRoot(StringBuilderNode root) {
+ WorkList<StringBuilderNode> workList = WorkList.newIdentityWorkList(root);
+ while (workList.hasNext()) {
+ StringBuilderNode node = workList.next();
+ if (!node.isSplitReferenceNode() && !node.isLoopNode()) {
+ return node;
+ }
+ if (node.hasSingleSuccessor()) {
+ workList.addIfNotSeen(node.getSingleSuccessor());
+ }
+ }
+ return root;
+ }
+
+ private static String getConstantArgumentForNode(
+ StringBuilderNode node, MunchingState munchingState) {
+ if (node.isAppendNode()) {
+ AppendNode appendNode = node.asAppendNode();
+ if (appendNode.hasConstantArgument()) {
+ return appendNode.getConstantArgument();
+ }
+ return getOptimizedConstantArgument(appendNode, munchingState);
+ } else if (node.isInitNode()) {
+ InitNode initNode = node.asInitNode();
+ if (initNode.hasConstantArgument()) {
+ return initNode.getConstantArgument();
+ }
+ return getOptimizedConstantArgument(initNode, munchingState);
+ }
+ return null;
+ }
+
+ private static String getOptimizedConstantArgument(
+ StringBuilderInstruction node, MunchingState munchingState) {
+ List<Value> inValues = node.getInstruction().inValues();
+ if (inValues.size() != 2) {
+ return null;
+ }
+ return munchingState.optimizedStrings.get(inValues.get(1).getAliasedValue());
+ }
+
+ /**
+ * This pattern tries to remove nodes that are no longer needed. An example is when a toString is
+ * materialized, and append may now not be observable and can be removed. This pattern tries to
+ * remove as many nodes as possibly by continuing to remove predecessor nodes. This is not
+ * necessary for correctness but since the overall munching algorithm runs over successors,
+ * removing predecessors directly here saves cycles.
+ */
+ private static class MunchNonMaterializing implements PeepholePattern {
+
+ @Override
+ public boolean optimize(
+ StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
+ boolean removedAnyNodes = false;
+ boolean removeNode;
+ boolean isEscaping = munchingState.escaping.contains(root);
+ while (currentNode != null) {
+ removeNode =
+ currentNode.isSplitReferenceNode()
+ && (currentNode.getSuccessors().isEmpty() || currentNode.hasSinglePredecessor());
+ // Remove appends if the string builder do not escape, is not inspected or materialized
+ if (removeNode) {
+ // TODO(b/190489514): See if this larger pattern is necessary.
+ assert false : "Check when this happens";
+ }
+ // and if it is not part of a loop.
+ if (currentNode.isAppendNode() && !isEscaping) {
+ AppendNode appendNode = currentNode.asAppendNode();
+ boolean canRemoveIfNoInspectionOrMaterializing =
+ !munchingState.inspectingCapacity.contains(root)
+ && munchingState.materializingInstructions.get(root).isEmpty();
+ boolean canRemoveIfLastAndNoLoop =
+ !isLoopingOnPath(root, currentNode, munchingState)
+ && currentNode.getSuccessors().isEmpty();
+ if (canRemoveIfNoInspectionOrMaterializing
+ && canRemoveIfLastAndNoLoop
+ && munchingState.oracle.canObserveStringBuilderCall(
+ currentNode.asAppendNode().getInstruction())) {
+ munchingState.actions.put(
+ appendNode.getInstruction(), RemoveStringBuilderAction.getInstance());
+ removeNode = true;
+ }
+ }
+ if (currentNode.isInitNode()
+ && currentNode.asInitNode().hasConstantArgument()
+ && currentNode.hasSinglePredecessor()
+ && currentNode.getSinglePredecessor().isNewInstanceNode()
+ && currentNode.getSuccessors().isEmpty()
+ && !isEscaping
+ && !munchingState.oracle.canObserveStringBuilderCall(
+ currentNode.asInitNode().getInstruction())) {
+ removeNode = true;
+ }
+ if (!removeNode) {
+ return false;
+ } else {
+ removedAnyNodes = true;
+ currentNode.removeNode();
+ if (currentNode.isStringBuilderInstructionNode()) {
+ munchingState.actions.put(
+ currentNode.asStringBuilderInstructionNode().getInstruction(),
+ RemoveStringBuilderAction.getInstance());
+ }
+ currentNode =
+ currentNode.hasSinglePredecessor() ? currentNode.getSinglePredecessor() : null;
+ }
+ }
+ return removedAnyNodes;
+ }
+
+ private boolean isLoopingOnPath(
+ StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
+ if (!munchingState.looping.contains(root)) {
+ return false;
+ }
+ WorkList<StringBuilderNode> workList = WorkList.newIdentityWorkList(currentNode);
+ boolean seenNewInstance = false;
+ while (workList.hasNext()) {
+ StringBuilderNode next = workList.next();
+ if (next.isNewInstanceNode()) {
+ seenNewInstance = true;
+ }
+ if (next.isLoopNode()) {
+ // If we have seen a new instance and there is only a single successor for the loop
+ // the instance is always created inside the body.
+ return !seenNewInstance || !next.hasSingleSuccessor();
+ }
+ workList.addIfNotSeen(next.getPredecessors());
+ }
+ return false;
+ }
+ }
+
+ private static final PeepholePattern[] peepholePatterns =
+ new PeepholePattern[] {new MunchAppends(), new MunchToString(), new MunchNonMaterializing()};
+
+ static boolean optimize(
+ StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
+ if (currentNode.isDead()) {
+ return false;
+ }
+ for (PeepholePattern peepholePattern : peepholePatterns) {
+ if (peepholePattern.optimize(root, currentNode, munchingState)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 3ad14fb..957ccfe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -722,7 +722,7 @@
return null;
}
if (StringBuilderAppendFlowAnalysis.hasAppendInstructionInLoop(
- code, builder, optimizationConfiguration)) {
+ appView, code, builder, optimizationConfiguration)) {
return null;
}
return StringUtils.join("", contents);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
new file mode 100644
index 0000000..b6c0b4f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+import static com.android.tools.r8.ir.optimize.string.StringBuilderHelper.extractConstantArgument;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexItemFactory.StringBuildingMethods;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import java.util.List;
+
+/**
+ * The {@link StringBuilderOracle} can answer if an instruction is of particular interest to the
+ * StringBuilderOptimization.
+ */
+interface StringBuilderOracle {
+
+ boolean isModeledStringBuilderInstruction(Instruction instruction);
+
+ boolean hasStringBuilderType(Value value);
+
+ boolean isStringBuilderType(DexType type);
+
+ boolean isToString(Instruction instruction);
+
+ String getConstantArgument(Instruction instruction);
+
+ boolean isInspecting(Instruction instruction);
+
+ boolean isAppend(Instruction instruction);
+
+ boolean canObserveStringBuilderCall(Instruction instruction);
+
+ boolean isInit(Instruction instruction);
+
+ class DefaultStringBuilderOracle implements StringBuilderOracle {
+
+ private final DexItemFactory factory;
+
+ DefaultStringBuilderOracle(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ @Override
+ public boolean isModeledStringBuilderInstruction(Instruction instruction) {
+ if (instruction.isNewInstance()) {
+ return isStringBuilderType(instruction.asNewInstance().getType());
+ } else if (instruction.isInvokeMethod()) {
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ return isStringBuildingMethod(factory.stringBuilderMethods, invokedMethod)
+ || isStringBuildingMethod(factory.stringBufferMethods, invokedMethod);
+ }
+ return false;
+ }
+
+ private boolean isStringBuildingMethod(StringBuildingMethods methods, DexMethod method) {
+ return methods.isAppendMethod(method)
+ || methods.isConstructorMethod(method)
+ || method == methods.toString
+ || method == methods.capacity;
+ }
+
+ @Override
+ public boolean hasStringBuilderType(Value value) {
+ return value.getType().isClassType()
+ && isStringBuilderType(value.getType().asClassType().getClassType());
+ }
+
+ @Override
+ public boolean isStringBuilderType(DexType type) {
+ return type == factory.stringBuilderType || type == factory.stringBufferType;
+ }
+
+ @Override
+ public boolean isToString(Instruction instruction) {
+ if (!instruction.isInvokeMethodWithReceiver()) {
+ return false;
+ }
+ InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ return factory.stringBuilderMethods.toString == invokedMethod
+ || factory.stringBufferMethods.toString == invokedMethod;
+ }
+
+ @Override
+ public String getConstantArgument(Instruction instruction) {
+ if (!instruction.isInvokeMethodWithReceiver()) {
+ return null;
+ }
+ if (isAppend(instruction)) {
+ return getConstantStringForAppend(instruction.asInvokeVirtual());
+ } else if (isInit(instruction)) {
+ return getConstantStringForInit(instruction.asInvokeDirect());
+ }
+ return null;
+ }
+
+ private DexType getAppendType(InvokeVirtual invokeMethodWithReceiver) {
+ DexMethod invokedMethod = invokeMethodWithReceiver.getInvokedMethod();
+ if (!factory.stringBuilderMethods.isAppendMethod(invokedMethod)
+ && !factory.stringBufferMethods.isAppendMethod(invokedMethod)) {
+ return null;
+ }
+ return invokedMethod.getParameter(0);
+ }
+
+ private String getConstantStringForAppend(InvokeVirtual invoke) {
+ DexType appendType = getAppendType(invoke);
+ Value arg = invoke.getFirstNonReceiverArgument().getAliasedValue();
+ return appendType != null
+ ? extractConstantArgument(factory, invoke.getInvokedMethod(), arg, appendType)
+ : null;
+ }
+
+ private String getConstantStringForInit(InvokeDirect invoke) {
+ assert invoke.isInvokeConstructor(factory);
+ List<Value> inValues = invoke.inValues();
+ if (inValues.size() == 1) {
+ return "";
+ } else if (inValues.size() == 2 && !invoke.getArgument(1).getType().isPrimitiveType()) {
+ Value arg = invoke.getArgument(1).getAliasedValue();
+ DexType argType = invoke.getInvokedMethod().getParameter(0);
+ return argType != null
+ ? extractConstantArgument(factory, invoke.getInvokedMethod(), arg, argType)
+ : null;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isInspecting(Instruction instruction) {
+ if (!instruction.isInvokeMethodWithReceiver()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethodWithReceiver().getInvokedMethod();
+ return factory.stringBuilderMethods.capacity == invokedMethod
+ || factory.stringBufferMethods.capacity == invokedMethod;
+ }
+
+ @Override
+ public boolean isAppend(Instruction instruction) {
+ if (!instruction.isInvokeMethod()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ return factory.stringBuilderMethods.isAppendMethod(invokedMethod)
+ || factory.stringBufferMethods.isAppendMethod(invokedMethod);
+ }
+
+ @Override
+ public boolean canObserveStringBuilderCall(Instruction instruction) {
+ assert isModeledStringBuilderInstruction(instruction);
+ if (!instruction.isInvokeMethod()) {
+ assert false : "Expecting a call to string builder";
+ return true;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ if (factory.stringBuilderMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
+ || factory.stringBufferMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
+ || factory.stringBuilderMethods.charSequenceConstructor == invokedMethod
+ || factory.stringBufferMethods.charSequenceConstructor == invokedMethod) {
+ assert instruction.inValues().size() == 2;
+ return instruction.inValues().get(1).getType().isStringType(factory);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isInit(Instruction instruction) {
+ if (!instruction.isInvokeDirect()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ return factory.stringBuilderMethods.isConstructorMethod(invokedMethod)
+ || factory.stringBufferMethods.isConstructorMethod(invokedMethod);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
index 10e267b..fb04da0 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
@@ -244,117 +244,4 @@
return standardCfCodeFromInstructions(instructions);
}
}
-
- public static class CollectionConversionCfCodeProvider extends NullableConversionCfCodeProvider {
-
- private final DexType collectionType;
- private final DexMethod conversion;
-
- public CollectionConversionCfCodeProvider(
- AppView<?> appView, DexType holder, DexType collectionType, DexMethod conversion) {
- super(appView, holder);
- this.collectionType = collectionType;
- this.conversion = conversion;
- }
-
- @Override
- public CfCode generateCfCode() {
- DexItemFactory factory = appView.dexItemFactory();
- List<CfInstruction> instructions = new ArrayList<>();
-
- // if (arg == null) { return null; }
- generateNullCheck(instructions);
- instructions.add(
- CfFrame.builder().appendLocal(FrameType.initialized(collectionType)).build());
-
- CfFrame frame =
- CfFrame.builder()
- .appendLocal(FrameType.initialized(collectionType))
- .appendLocal(FrameType.initialized(collectionType))
- .appendLocal(FrameType.initialized(factory.iteratorType))
- .build();
-
- // Collection<E> t1 = new Collection<E>();
- if (collectionType == factory.setType) {
- DexType hashSetType = factory.createType("Ljava/util/HashSet;");
- instructions.add(new CfNew(hashSetType));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKESPECIAL,
- factory.createMethod(
- hashSetType,
- factory.createProto(factory.voidType),
- factory.constructorMethodName),
- false));
- } else {
- assert collectionType == factory.listType;
- DexType arrayListType = factory.createType("Ljava/util/ArrayList;");
- instructions.add(new CfNew(arrayListType));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKESPECIAL,
- factory.createMethod(
- arrayListType,
- factory.createProto(factory.voidType),
- factory.constructorMethodName),
- false));
- }
- instructions.add(new CfStore(ValueType.OBJECT, 1));
-
- // Iterator<E> t2 = receiver.iterator();
- instructions.add(new CfLoad(ValueType.OBJECT, 0));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKEINTERFACE,
- factory.createMethod(
- factory.collectionType, factory.createProto(factory.iteratorType), "iterator"),
- true));
- instructions.add(new CfStore(ValueType.OBJECT, 2));
-
- // while(t2.hasNext())
- CfLabel returnLabel = new CfLabel();
- CfLabel loopLabel = new CfLabel();
- instructions.add(loopLabel);
- instructions.add(frame);
- instructions.add(new CfLoad(ValueType.fromDexType(factory.iteratorType), 2));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKEINTERFACE,
- factory.createMethod(
- factory.iteratorType, factory.createProto(factory.booleanType), "hasNext"),
- true));
- instructions.add(new CfConstNumber(0, ValueType.INT));
- instructions.add(new CfIfCmp(If.Type.EQ, ValueType.INT, returnLabel));
-
- // {t1.add(convert(t2.next());}
- instructions.add(new CfLoad(ValueType.fromDexType(collectionType), 1));
- instructions.add(new CfLoad(ValueType.fromDexType(factory.iteratorType), 2));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKEINTERFACE,
- factory.createMethod(
- factory.iteratorType,
- factory.createProto(conversion.getArgumentType(0, true)),
- "next"),
- true));
- instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, conversion, false));
- instructions.add(
- new CfInvoke(
- Opcodes.INVOKEINTERFACE,
- factory.createMethod(
- factory.collectionType,
- factory.createProto(factory.booleanType, factory.objectType),
- "add"),
- true));
- instructions.add(new CfGoto(loopLabel));
-
- // return t1;
- instructions.add(returnLabel);
- instructions.add(frame.clone());
- instructions.add(new CfLoad(ValueType.fromDexType(collectionType), 1));
- instructions.add(new CfReturn(ValueType.fromDexType(collectionType)));
-
- return standardCfCodeFromInstructions(instructions);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index cdb9762..ab83fdb 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -364,13 +364,13 @@
if (!method.hasClassFileVersion()) {
// In this case bridges have been introduced for the Cf back-end,
// which do not have class file version.
- assert options.isDesugaredLibraryCompilation() || options.cfToCfDesugar
+ assert options.isDesugaredLibraryCompilation() || options.isDesugaring()
: "Expected class file version for " + method.getReference().toSourceString();
assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(
options.classFileVersionAfterDesugaring(InternalOptions.SUPPORTED_CF_VERSION));
// Any desugaring rewrites which cannot meet the default class file version after
// desugaring must upgrade the class file version during desugaring.
- return options.cfToCfDesugar
+ return options.isDesugaring()
? options.classFileVersionAfterDesugaring(InternalOptions.SUPPORTED_CF_VERSION)
: MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
}
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 9f01beb..9591d26 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -65,7 +65,6 @@
private ImmutableMap<String, ClassNamingForNameMapper> buildClassNameMappings() {
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
- builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
mapping.forEach(
(renamedName, valueBuilder) -> builder.put(renamedName, valueBuilder.build()));
return builder.build();
@@ -158,6 +157,7 @@
public static ClassNameMapper mapperFromLineReaderWithFiltering(
LineReader reader,
+ MapVersion mapVersion,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping)
@@ -167,7 +167,8 @@
reader,
diagnosticsHandler != null ? diagnosticsHandler : new Reporter(),
allowEmptyMappedRanges,
- allowExperimentalMapping)) {
+ allowExperimentalMapping,
+ mapVersion)) {
ClassNameMapper.Builder builder = ClassNameMapper.builder();
proguardReader.parse(builder);
return builder.build();
@@ -177,12 +178,12 @@
private final ImmutableMap<String, ClassNamingForNameMapper> classNameMappings;
private BiMapContainer<String, String> nameMapping;
private final Map<Signature, Signature> signatureMap = new HashMap<>();
- private final Set<MapVersionMappingInformation> mapVersions;
+ private final LinkedHashSet<MapVersionMappingInformation> mapVersions;
private final Map<String, String> originalSourceFiles;
private ClassNameMapper(
ImmutableMap<String, ClassNamingForNameMapper> classNameMappings,
- Set<MapVersionMappingInformation> mapVersions,
+ LinkedHashSet<MapVersionMappingInformation> mapVersions,
Map<String, String> originalSourceFiles) {
this.classNameMappings = classNameMappings;
this.mapVersions = mapVersions;
@@ -242,6 +243,40 @@
return originalSourceFiles.get(typeName);
}
+ public ClassNameMapper combine(ClassNameMapper other) {
+ if (other == null || other.isEmpty()) {
+ return this;
+ }
+ if (this.isEmpty()) {
+ return other;
+ }
+ ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
+ Map<String, ClassNamingForNameMapper> otherClassMappings = other.getClassNameMappings();
+ for (Entry<String, ClassNamingForNameMapper> mappingEntry : classNameMappings.entrySet()) {
+ ClassNamingForNameMapper otherMapping = otherClassMappings.get(mappingEntry.getKey());
+ if (otherMapping == null) {
+ builder.put(mappingEntry);
+ } else {
+ builder.put(mappingEntry.getKey(), mappingEntry.getValue().combine(otherMapping));
+ }
+ }
+ otherClassMappings.forEach(
+ (otherMappingClass, otherMapping) -> {
+ // Collisions are handled above so only take non-existing mappings.
+ if (!classNameMappings.containsKey(otherMappingClass)) {
+ builder.put(otherMappingClass, otherMapping);
+ }
+ });
+ LinkedHashSet<MapVersionMappingInformation> newMapVersions =
+ new LinkedHashSet<>(getMapVersions());
+ newMapVersions.addAll(other.getMapVersions());
+ Map<String, String> newSourcesFiles = new HashMap<>(originalSourceFiles);
+ // This will overwrite existing source files but the chance of that happening should be very
+ // slim.
+ newSourcesFiles.putAll(other.originalSourceFiles);
+ return new ClassNameMapper(builder.build(), newMapVersions, newSourcesFiles);
+ }
+
@Override
public boolean hasMapping(DexType type) {
String decoded = descriptorToJavaType(type.descriptor.toString());
@@ -382,4 +417,8 @@
public Set<MapVersionMappingInformation> getMapVersions() {
return mapVersions;
}
+
+ public MapVersionMappingInformation getFirstMappingInformation() {
+ return mapVersions.isEmpty() ? null : mapVersions.iterator().next();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index c1e5de7..352c555 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -265,6 +265,40 @@
return mappedRangesByRenamedName.get(renamedName);
}
+ public boolean isEmpty() {
+ return methodMembers.isEmpty() && fieldMembers.isEmpty();
+ }
+
+ public ClassNamingForNameMapper combine(ClassNamingForNameMapper otherMapping) {
+ if (!originalName.equals(otherMapping.originalName)) {
+ throw new RuntimeException(
+ "Cannot combine mapping for "
+ + renamedName
+ + " because it maps back to both "
+ + originalName
+ + " and "
+ + otherMapping.originalName
+ + ".");
+ }
+ if (!renamedName.equals(otherMapping.renamedName)) {
+ throw new RuntimeException(
+ "Cannot combine mapping for "
+ + originalName
+ + " because it maps forward to both "
+ + originalName
+ + " and "
+ + otherMapping.originalName
+ + ".");
+ }
+ if (this.isEmpty()) {
+ return otherMapping;
+ } else if (otherMapping.isEmpty()) {
+ return this;
+ } else {
+ throw new RuntimeException("R8 Retrace do not support merging of partial class mappings.");
+ }
+ }
+
@Override
public MemberNaming lookup(Signature renamedSignature) {
if (renamedSignature.kind() == SignatureKind.METHOD) {
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 f28d4bc..3eb0ec1 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -77,10 +77,25 @@
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping) {
+ this(
+ reader,
+ diagnosticsHandler,
+ allowEmptyMappedRanges,
+ allowExperimentalMapping,
+ MapVersion.MAP_VERSION_NONE);
+ }
+
+ ProguardMapReader(
+ LineReader reader,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean allowEmptyMappedRanges,
+ boolean allowExperimentalMapping,
+ MapVersion mapVersion) {
this.reader = reader;
this.diagnosticsHandler = diagnosticsHandler;
this.allowEmptyMappedRanges = allowEmptyMappedRanges;
this.allowExperimentalMapping = allowExperimentalMapping;
+ this.version = mapVersion;
assert reader != null;
assert diagnosticsHandler != null;
}
@@ -89,7 +104,7 @@
private int lineNo = 0;
private int lineOffset = 0;
private String line;
- private MapVersion version = MapVersion.MAP_VERSION_NONE;
+ private MapVersion version;
private int peekCodePoint() {
return lineOffset < line.length() ? line.codePointAt(lineOffset) : '\n';
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 2efdb3c..2ac83b2 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.TriConsumer;
@@ -35,6 +36,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -354,15 +356,32 @@
(bridgeHolder, targets) -> {
// Sorting the list of bridges within a class maintains a deterministic order of entries
// in the method collection.
- targets.sort((p1, p2) -> p1.getFirst().compareTo(p2.getFirst()));
+ targets.sort(Comparator.comparing(Pair::getFirst));
for (Pair<DexMethod, DexClassAndMethod> pair : targets) {
DexMethod method = pair.getFirst();
DexClassAndMethod target = pair.getSecond();
DexMethod bridgeMethod =
method.withHolder(bridgeHolder.getType(), appView.dexItemFactory());
if (bridgeHolder.getMethodCollection().getMethod(bridgeMethod) == null) {
+ DexEncodedMethod targetDefinition = target.getDefinition();
DexEncodedMethod bridgeMethodDefinition =
- target.getDefinition().toForwardingMethod(bridgeHolder, appView);
+ targetDefinition.toForwardingMethod(
+ bridgeHolder,
+ appView,
+ builder -> {
+ if (!targetDefinition.isAbstract()
+ && targetDefinition.getApiLevelForCode().isNotSetApiLevel()) {
+ assert target.isLibraryMethod();
+ builder.setApiLevelForCode(
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReference(
+ targetDefinition.getReference(),
+ appView.computedMinApiLevel()));
+ }
+ builder.setIsLibraryMethodOverrideIf(
+ target.isLibraryMethod(), OptionalBool.TRUE);
+ });
bridgeHolder.addMethod(bridgeMethodDefinition);
}
assert resolver.apply(method).getResolvedMethod().getReference() == bridgeMethod;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index bd3fee8..df0f324 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.AccessUtils;
+import com.android.tools.r8.utils.AndroidApiLevelUtils;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
@@ -642,8 +643,9 @@
private DexType getNewFieldType(ProgramField field) {
DynamicType dynamicType = field.getOptimizationInfo().getDynamicType();
+ DexType staticType = field.getType();
if (dynamicType.isUnknown()) {
- return field.getType();
+ return staticType;
}
KeepFieldInfo keepInfo = appView.getKeepInfo(field);
@@ -652,17 +654,17 @@
assert !keepInfo.isPinned(options);
if (!keepInfo.isFieldTypeStrengtheningAllowed(options)) {
- return field.getType();
+ return staticType;
}
if (dynamicType.isNullType()) {
// Don't optimize always null fields; these will be optimized anyway.
- return field.getType();
+ return staticType;
}
if (dynamicType.isNotNullType()) {
// We don't have a more specific type.
- return field.getType();
+ return staticType;
}
DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
@@ -670,15 +672,15 @@
TypeElement dynamicUpperBoundType = dynamicTypeWithUpperBound.getDynamicUpperBoundType();
assert dynamicUpperBoundType.isReferenceType();
- ClassTypeElement staticFieldType = field.getType().toTypeElement(appView).asClassType();
+ ClassTypeElement staticFieldType = staticType.toTypeElement(appView).asClassType();
if (dynamicUpperBoundType.equalUpToNullability(staticFieldType)) {
// We don't have more precise type information.
- return field.getType();
+ return staticType;
}
if (!dynamicUpperBoundType.strictlyLessThan(staticFieldType, appView)) {
assert options.testing.allowTypeErrors;
- return field.getType();
+ return staticType;
}
DexType newStaticFieldType;
@@ -689,7 +691,7 @@
newStaticFieldType =
dynamicUpperBoundClassType.getInterfaces().getSingleKnownInterface();
} else {
- return field.getType();
+ return staticType;
}
} else {
newStaticFieldType = dynamicUpperBoundClassType.getClassType();
@@ -698,8 +700,17 @@
newStaticFieldType = dynamicUpperBoundType.asArrayType().toDexType(dexItemFactory);
}
- if (!AccessUtils.isAccessibleInSameContextsAs(newStaticFieldType, field.getType(), appView)) {
- return field.getType();
+ if (newStaticFieldType == staticType) {
+ return staticType;
+ }
+
+ if (!AccessUtils.isAccessibleInSameContextsAs(newStaticFieldType, staticType, appView)) {
+ return staticType;
+ }
+
+ if (!AndroidApiLevelUtils.isApiSafeForTypeStrengthening(
+ newStaticFieldType, staticType, appView)) {
+ return staticType;
}
return newStaticFieldType;
@@ -965,10 +976,13 @@
if (!appView.appInfo().isSubtype(newReturnType, staticType)) {
return null;
}
- return AccessUtils.isAccessibleInSameContextsAs(
- newReturnType, method.getReturnType(), appView)
- ? newReturnType
- : null;
+ if (!AccessUtils.isAccessibleInSameContextsAs(newReturnType, staticType, appView)) {
+ return null;
+ }
+ if (!AndroidApiLevelUtils.isApiSafeForTypeStrengthening(newReturnType, staticType, appView)) {
+ return null;
+ }
+ return newReturnType;
}
private SingleValue getReturnValue(ProgramMethod method) {
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
index b381e18..3684506 100644
--- 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
@@ -30,18 +30,18 @@
}
@Override
- public CfFrameState check(AppView<?> appView, CfFrame frame) {
- return new ConcreteCfFrameState().check(appView, frame);
+ public CfFrameState check(CfAnalysisConfig config, CfFrame frame) {
+ return this;
}
@Override
- public CfFrameState checkLocals(AppView<?> appView, CfFrame frame) {
- return new ConcreteCfFrameState().checkLocals(appView, frame);
+ public CfFrameState checkLocals(CfAnalysisConfig config, CfFrame frame) {
+ return this;
}
@Override
- public CfFrameState checkStack(AppView<?> appView, CfFrame frame) {
- return new ConcreteCfFrameState().checkStack(appView, frame);
+ public CfFrameState checkStack(CfAnalysisConfig config, CfFrame frame) {
+ return this;
}
@Override
@@ -52,66 +52,73 @@
@Override
public CfFrameState markInitialized(
UninitializedFrameType uninitializedType, DexType initializedType) {
- // Initializing an uninitialized type is a no-op when the frame is empty.
return this;
}
@Override
- public ErroneousCfFrameState pop() {
- return error("Unexpected pop from empty stack");
+ public CfFrameState pop() {
+ return this;
}
@Override
- public ErroneousCfFrameState pop(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
- return pop();
+ public CfFrameState pop(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
+ return this;
}
@Override
- public ErroneousCfFrameState popAndInitialize(
+ public CfFrameState popAndInitialize(
AppView<?> appView, DexMethod constructor, CfAnalysisConfig config) {
- return pop();
+ return this;
}
@Override
- public ErroneousCfFrameState popArray(AppView<?> appView) {
- return pop();
+ public CfFrameState popArray(AppView<?> appView) {
+ return this;
}
@Override
- public ErroneousCfFrameState popInitialized(
+ public CfFrameState popInitialized(
AppView<?> appView,
+ CfAnalysisConfig config,
DexType expectedType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
- return pop();
+ return this;
}
@Override
- public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) {
- return expectedTypes.length == 0 ? this : pop();
+ public CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, DexType... expectedTypes) {
+ return this;
}
@Override
public CfFrameState push(CfAnalysisConfig config, DexType type) {
- return new ConcreteCfFrameState().push(config, type);
+ return this;
}
@Override
public CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType) {
- return new ConcreteCfFrameState().push(config, frameType);
+ return this;
}
@Override
- public ErroneousCfFrameState readLocal(
+ public CfFrameState pushException(CfAnalysisConfig config, DexType guard) {
+ return this;
+ }
+
+ @Override
+ public CfFrameState readLocal(
AppView<?> appView,
+ CfAnalysisConfig config,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
- return error("Unexpected local read from empty frame");
+ return this;
}
@Override
public CfFrameState storeLocal(int localIndex, FrameType frameType, CfAnalysisConfig config) {
- return new ConcreteCfFrameState().storeLocal(localIndex, frameType, config);
+ return this;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
index bd8a7c9..0a2bec0 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfAnalysisConfig.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.optimize.interfaces.analysis;
+import com.android.tools.r8.cf.code.CfAssignability;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
public interface CfAnalysisConfig {
+ CfAssignability getAssignability();
+
DexMethod getCurrentContext();
int getMaxLocals();
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
index dfa38f5..b136b9b 100644
--- 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
@@ -12,6 +12,7 @@
import com.android.tools.r8.cf.code.frame.FrameType;
import com.android.tools.r8.cf.code.frame.PreciseFrameType;
import com.android.tools.r8.cf.code.frame.UninitializedFrameType;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -72,11 +73,13 @@
}
@Override
- public boolean isGreaterThanOrEquals(CfFrameState state) {
+ public boolean isGreaterThanOrEquals(AppView<?> appView, CfFrameState state) {
if (this == state) {
return true;
}
- CfFrameState leastUpperBound = join(state, UnaryOperator.identity());
+ assert appView.hasClassHierarchy();
+ CfFrameState leastUpperBound =
+ join(appView.withClassHierarchy(), state, UnaryOperator.identity());
return equals(leastUpperBound);
}
@@ -100,11 +103,11 @@
return null;
}
- public abstract CfFrameState check(AppView<?> appView, CfFrame frame);
+ public abstract CfFrameState check(CfAnalysisConfig config, CfFrame frame);
- public abstract CfFrameState checkLocals(AppView<?> appView, CfFrame frame);
+ public abstract CfFrameState checkLocals(CfAnalysisConfig config, CfFrame frame);
- public abstract CfFrameState checkStack(AppView<?> appView, CfFrame frame);
+ public abstract CfFrameState checkStack(CfAnalysisConfig config, CfFrame frame);
public abstract CfFrameState clear();
@@ -120,38 +123,46 @@
public abstract CfFrameState popArray(AppView<?> appView);
- public final CfFrameState popInitialized(AppView<?> appView, DexType expectedType) {
- return popInitialized(appView, expectedType, FunctionUtils::getFirst);
+ public final CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, DexType expectedType) {
+ return popInitialized(appView, config, expectedType, FunctionUtils::getFirst);
}
public abstract CfFrameState popInitialized(
AppView<?> appView,
+ CfAnalysisConfig config,
DexType expectedType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn);
- public abstract CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes);
+ public abstract CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, DexType... expectedTypes);
- public final CfFrameState popInitialized(AppView<?> appView, MemberType memberType) {
+ public final CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, MemberType memberType) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return popInitialized(
appView,
+ config,
FrameType.fromPreciseMemberType(memberType, dexItemFactory)
.getInitializedType(dexItemFactory));
}
- public final CfFrameState popInitialized(AppView<?> appView, NumericType expectedType) {
- return popInitialized(appView, expectedType.toDexType(appView.dexItemFactory()));
+ public final CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, NumericType expectedType) {
+ return popInitialized(appView, config, expectedType.toDexType(appView.dexItemFactory()));
}
- public final CfFrameState popInitialized(AppView<?> appView, ValueType valueType) {
- return popInitialized(appView, valueType, FunctionUtils::getFirst);
+ public final CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, ValueType valueType) {
+ return popInitialized(appView, config, valueType, FunctionUtils::getFirst);
}
public final CfFrameState popInitialized(
AppView<?> appView,
+ CfAnalysisConfig config,
ValueType valueType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
- return popInitialized(appView, valueType.toDexType(appView.dexItemFactory()), fn);
+ return popInitialized(appView, config, valueType.toDexType(appView.dexItemFactory()), fn);
}
public final CfFrameState popObject(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
@@ -162,17 +173,16 @@
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState popObject(
- AppView<?> appView,
DexType expectedType,
CfAnalysisConfig config,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
+ CfAssignability assignability = config.getAssignability();
return pop(
(state, head) ->
head.isObject()
- && CfAssignability.isAssignable(
+ && assignability.isAssignable(
head.getObjectType(config.getCurrentContext().getHolderType()),
- expectedType,
- appView)
+ expectedType)
? fn.apply(state, head)
: errorUnexpectedStack(head, expectedType));
}
@@ -288,8 +298,11 @@
return push(config, valueType.toDexType(appView.dexItemFactory()));
}
+ public abstract CfFrameState pushException(CfAnalysisConfig config, DexType guard);
+
public abstract CfFrameState readLocal(
AppView<?> appView,
+ CfAnalysisConfig config,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> consumer);
@@ -313,13 +326,18 @@
}
@Override
- public final CfFrameState join(CfFrameState state) {
+ public final CfFrameState join(AppView<?> appView, CfFrameState state) {
+ assert appView.hasClassHierarchy();
return join(
- state, frameType -> frameType.isSingle() ? FrameType.oneWord() : FrameType.twoWord());
+ appView.withClassHierarchy(),
+ state,
+ frameType -> frameType.isSingle() ? FrameType.oneWord() : FrameType.twoWord());
}
public final CfFrameState join(
- CfFrameState state, UnaryOperator<FrameType> joinWithMissingLocal) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ CfFrameState state,
+ UnaryOperator<FrameType> joinWithMissingLocal) {
if (state.isBottom() || isError()) {
return this;
}
@@ -328,7 +346,7 @@
}
assert isConcrete();
assert state.isConcrete();
- return asConcrete().join(state.asConcrete(), joinWithMissingLocal);
+ return asConcrete().join(appView, state.asConcrete(), joinWithMissingLocal);
}
@Override
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
index 33e4cf2..ae2e594 100644
--- 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
@@ -4,10 +4,14 @@
package com.android.tools.r8.optimize.interfaces.analysis;
+import com.android.tools.r8.cf.code.CfAssignability;
import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfSubtypingAssignability;
+import com.android.tools.r8.cf.code.frame.FrameType;
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.DexClassAndMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -20,51 +24,124 @@
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;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
public class CfOpenClosedInterfacesAnalysis {
private final AppView<AppInfoWithLiveness> appView;
+ private final CfAssignability assignability;
+ private final InternalOptions options;
+
+ private final ProgramMethodMap<UnverifiableCfCodeDiagnostic> unverifiableCodeDiagnostics =
+ ProgramMethodMap.createConcurrent();
public CfOpenClosedInterfacesAnalysis(AppView<AppInfoWithLiveness> appView) {
+ InternalOptions options = appView.options();
this.appView = appView;
+ this.assignability = new CfSubtypingAssignability(appView);
+ this.options = options;
}
- public void run() {
- // TODO(b/214496607): Parallelize the analysis.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod);
- }
+ public boolean run(ExecutorService executorService) throws ExecutionException {
+ processClasses(executorService);
+ reportUnverifiableCodeDiagnostics();
+ return true;
+ }
+
+ private void processClasses(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+ }
+
+ private void processClass(DexProgramClass clazz) {
+ clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod);
}
private void processMethod(ProgramMethod method) {
Code code = method.getDefinition().getCode();
if (!code.isCfCode()) {
- assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
+ assert code.isDefaultInstanceInitializerCode() || code.isDexCode() || code.isThrowNullCode();
return;
}
CfCode cfCode = code.asCfCode();
- CfControlFlowGraph cfg = CfControlFlowGraph.create(cfCode);
+ CfControlFlowGraph cfg = CfControlFlowGraph.create(cfCode, options);
+ TransferFunction transfer = new TransferFunction(method);
CfIntraproceduralDataflowAnalysis<CfFrameState> analysis =
- new CfIntraproceduralDataflowAnalysis<>(
- CfFrameState.bottom(), cfg, new TransferFunction(method));
+ new CfIntraproceduralDataflowAnalysis<>(appView, CfFrameState.bottom(), cfg, transfer);
DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock());
- // TODO(b/214496607): Determine open interfaces.
+ assert result.isSuccessfulAnalysisResult();
+ for (CfBlock block : cfg.getBlocks()) {
+ if (analysis.isIntermediateBlock(block)) {
+ continue;
+ }
+ CfFrameState state = analysis.computeBlockEntryState(block);
+ do {
+ for (int instructionIndex = block.getFirstInstructionIndex();
+ instructionIndex <= block.getLastInstructionIndex();
+ instructionIndex++) {
+ // TODO(b/214496607): Determine open interfaces.
+ CfInstruction instruction = cfCode.getInstruction(instructionIndex);
+ state = transfer.apply(instruction, state).asAbstractState();
+ if (state.isError()) {
+ if (options.getCfCodeAnalysisOptions().isUnverifiableCodeReportingEnabled()) {
+ unverifiableCodeDiagnostics.put(
+ method,
+ new UnverifiableCfCodeDiagnostic(
+ method.getMethodReference(),
+ instructionIndex,
+ state.asError().getMessage(),
+ method.getOrigin()));
+ }
+ return;
+ }
+ }
+ if (analysis.isBlockWithIntermediateSuccessorBlock(block)) {
+ block = cfg.getUniqueSuccessor(block);
+ } else {
+ block = null;
+ }
+ } while (block != null);
+ }
+ }
+
+ private void reportUnverifiableCodeDiagnostics() {
+ Reporter reporter = appView.reporter();
+ List<ProgramMethod> methods = new ArrayList<>(unverifiableCodeDiagnostics.size());
+ unverifiableCodeDiagnostics.forEach((method, diagnostic) -> methods.add(method));
+ methods.sort(Comparator.comparing(DexClassAndMember::getReference));
+ methods.forEach(method -> reporter.warning(unverifiableCodeDiagnostics.get(method)));
}
private class TransferFunction
implements AbstractTransferFunction<CfBlock, CfInstruction, CfFrameState> {
+ private final CfCode code;
private final CfAnalysisConfig config;
+ private final ProgramMethod context;
TransferFunction(ProgramMethod context) {
CfCode code = context.getDefinition().getCode().asCfCode();
int maxLocals = code.getMaxLocals();
int maxStack = code.getMaxStack();
+ this.code = code;
this.config =
new CfAnalysisConfig() {
@Override
+ public CfAssignability getAssignability() {
+ return assignability;
+ }
+
+ @Override
public DexMethod getCurrentContext() {
return context.getReference();
}
@@ -84,12 +161,35 @@
return type == context.getHolder().getSuperType();
}
};
+ this.context = context;
}
@Override
public TransferFunctionResult<CfFrameState> apply(
CfInstruction instruction, CfFrameState state) {
- return instruction.evaluate(state, appView, config, appView.dexItemFactory());
+ return instruction.evaluate(state, appView, config);
+ }
+
+ @Override
+ public CfFrameState computeInitialState(CfBlock entryBlock, CfFrameState bottom) {
+ CfFrameState initialState = new ConcreteCfFrameState();
+ int localIndex = 0;
+ if (context.getDefinition().isInstance()) {
+ if (context.getDefinition().isInstanceInitializer()) {
+ initialState = initialState.storeLocal(localIndex, FrameType.uninitializedThis(), config);
+ } else {
+ initialState =
+ initialState.storeLocal(
+ localIndex, FrameType.initialized(context.getHolderType()), config);
+ }
+ localIndex++;
+ }
+ for (DexType parameter : context.getParameters()) {
+ initialState =
+ initialState.storeLocal(localIndex, FrameType.initialized(parameter), config);
+ localIndex += parameter.getRequiredRegisters();
+ }
+ return initialState;
}
@Override
@@ -97,5 +197,15 @@
CfBlock block, CfBlock predecessor, CfFrameState predecessorExitState) {
return predecessorExitState;
}
+
+ @Override
+ public CfFrameState computeExceptionalBlockEntryState(
+ CfBlock block,
+ DexType guard,
+ CfBlock throwBlock,
+ CfInstruction throwInstruction,
+ CfFrameState throwState) {
+ return throwState.pushException(config, guard);
+ }
}
}
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
index f05ab67..531db8b 100644
--- 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
@@ -10,11 +10,14 @@
import com.android.tools.r8.cf.code.CfAssignability;
import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Builder;
+import com.android.tools.r8.cf.code.CfFrameUtils;
import com.android.tools.r8.cf.code.frame.FrameType;
import com.android.tools.r8.cf.code.frame.PreciseFrameType;
import com.android.tools.r8.cf.code.frame.SingleFrameType;
import com.android.tools.r8.cf.code.frame.UninitializedFrameType;
import com.android.tools.r8.cf.code.frame.WideFrameType;
+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.graph.DexType;
@@ -40,12 +43,13 @@
private final ArrayDeque<PreciseFrameType> stack;
private int stackHeight;
- ConcreteCfFrameState() {
+ public ConcreteCfFrameState() {
this(new Int2ObjectAVLTreeMap<>(), new ArrayDeque<>(), 0);
}
public ConcreteCfFrameState(
Int2ObjectAVLTreeMap<FrameType> locals, ArrayDeque<PreciseFrameType> stack, int stackHeight) {
+ assert CfFrameUtils.verifyLocals(locals);
this.locals = locals;
this.stack = stack;
this.stackHeight = stackHeight;
@@ -67,10 +71,10 @@
}
@Override
- public CfFrameState check(AppView<?> appView, CfFrame frame) {
+ public CfFrameState check(CfAnalysisConfig config, CfFrame frame) {
CfFrame currentFrame = CfFrame.builder().setLocals(locals).setStack(stack).build();
AssignabilityResult assignabilityResult =
- CfAssignability.isFrameAssignable(currentFrame, frame, appView);
+ config.getAssignability().isFrameAssignable(currentFrame, frame);
if (assignabilityResult.isFailed()) {
return error(assignabilityResult.asFailed().getMessage());
}
@@ -80,9 +84,9 @@
}
@Override
- public CfFrameState checkLocals(AppView<?> appView, CfFrame frame) {
+ public CfFrameState checkLocals(CfAnalysisConfig config, CfFrame frame) {
AssignabilityResult assignabilityResult =
- CfAssignability.isLocalsAssignable(locals, frame.getLocals(), appView);
+ config.getAssignability().isLocalsAssignable(locals, frame.getLocals());
if (assignabilityResult.isFailed()) {
return error(assignabilityResult.asFailed().getMessage());
}
@@ -90,9 +94,9 @@
}
@Override
- public CfFrameState checkStack(AppView<?> appView, CfFrame frame) {
+ public CfFrameState checkStack(CfAnalysisConfig config, CfFrame frame) {
AssignabilityResult assignabilityResult =
- CfAssignability.isStackAssignable(stack, frame.getStack(), appView);
+ config.getAssignability().isStackAssignable(stack, frame.getStack());
if (assignabilityResult.isFailed()) {
return error(assignabilityResult.asFailed().getMessage());
}
@@ -139,8 +143,7 @@
@Override
public CfFrameState pop(BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
if (stack.isEmpty()) {
- // Return the same error as when popping from the bottom state.
- return bottom().pop();
+ return error("Unexpected pop from empty stack");
}
PreciseFrameType frameType = stack.removeLast();
stackHeight -= frameType.getWidth();
@@ -209,13 +212,15 @@
@Override
public CfFrameState popInitialized(
AppView<?> appView,
+ CfAnalysisConfig config,
DexType expectedType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
+ CfAssignability assignability = config.getAssignability();
return pop(
(state, frameType) -> {
if (frameType.isInitialized()) {
DexType initializedType = frameType.getInitializedType(appView.dexItemFactory());
- if (CfAssignability.isAssignable(initializedType, expectedType, appView)) {
+ if (assignability.isAssignable(initializedType, expectedType)) {
return fn.apply(state, frameType);
}
}
@@ -224,10 +229,11 @@
}
@Override
- public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) {
+ public CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, DexType... expectedTypes) {
CfFrameState state = this;
for (int i = expectedTypes.length - 1; i >= 0; i--) {
- state = state.popInitialized(appView, expectedTypes[i]);
+ state = state.popInitialized(appView, config, expectedTypes[i]);
}
return state;
}
@@ -259,8 +265,18 @@
}
@Override
+ public CfFrameState pushException(CfAnalysisConfig config, DexType guard) {
+ Int2ObjectAVLTreeMap<FrameType> newLocals = new Int2ObjectAVLTreeMap<>(locals);
+ ArrayDeque<PreciseFrameType> newStack = new ArrayDeque<>();
+ int newStackHeight = 0;
+ return new ConcreteCfFrameState(newLocals, newStack, newStackHeight)
+ .push(config, FrameType.initialized(guard));
+ }
+
+ @Override
public CfFrameState readLocal(
AppView<?> appView,
+ CfAnalysisConfig config,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
@@ -269,8 +285,9 @@
return error("Unexpected read of missing local at index " + localIndex);
}
if (frameType.isInitialized()) {
- if (CfAssignability.isAssignable(
- frameType.getInitializedType(appView.dexItemFactory()), expectedType, appView)) {
+ CfAssignability assignability = config.getAssignability();
+ DexType actualType = frameType.getInitializedType(appView.dexItemFactory());
+ if (assignability.isAssignable(actualType, expectedType)) {
return fn.apply(this, frameType);
}
} else if (frameType.isUninitialized() && expectedType.isObject()) {
@@ -285,10 +302,7 @@
if (maxLocalIndex >= config.getMaxLocals()) {
return storeLocalError(localIndex, frameType, config);
}
- locals.put(localIndex, frameType);
- if (frameType.isWide()) {
- locals.put(localIndex + 1, frameType);
- }
+ CfFrameUtils.storeLocal(localIndex, frameType, locals);
return this;
}
@@ -308,10 +322,12 @@
}
public CfFrameState join(
- ConcreteCfFrameState state, UnaryOperator<FrameType> joinWithMissingLocal) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ConcreteCfFrameState state,
+ UnaryOperator<FrameType> joinWithMissingLocal) {
CfFrame.Builder builder = CfFrame.builder();
- joinLocals(state.locals, builder, joinWithMissingLocal);
- ErroneousCfFrameState error = joinStack(state.stack, builder);
+ joinLocals(appView, state.locals, builder, joinWithMissingLocal);
+ ErroneousCfFrameState error = joinStack(appView, state.stack, builder);
if (error != null) {
return error;
}
@@ -320,8 +336,9 @@
}
private void joinLocals(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
Int2ObjectSortedMap<FrameType> locals,
- CfFrame.Builder builder,
+ Builder builder,
UnaryOperator<FrameType> joinWithMissingLocal) {
ObjectBidirectionalIterator<Entry<FrameType>> iterator =
this.locals.int2ObjectEntrySet().iterator();
@@ -356,7 +373,7 @@
builder);
} else {
joinLocalsWithSameIndex(
- localIndex, frameType, otherFrameType, iterator, otherIterator, builder);
+ localIndex, frameType, otherFrameType, iterator, otherIterator, appView, builder);
}
}
joinLocalsOnlyPresentInOne(iterator, builder, joinWithMissingLocal);
@@ -398,11 +415,12 @@
FrameType otherFrameType,
ObjectBidirectionalIterator<Entry<FrameType>> iterator,
ObjectBidirectionalIterator<Entry<FrameType>> otherIterator,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
CfFrame.Builder builder) {
if (frameType.isSingle()) {
if (otherFrameType.isSingle()) {
joinSingleLocalsWithSameIndex(
- localIndex, frameType.asSingle(), otherFrameType.asSingle(), builder);
+ localIndex, frameType.asSingle(), otherFrameType.asSingle(), appView, builder);
} else {
setWideLocalToTop(localIndex, builder);
handleOverlappingLocals(localIndex + 1, iterator, otherIterator, builder);
@@ -422,8 +440,9 @@
int localIndex,
SingleFrameType frameType,
SingleFrameType otherFrameType,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
CfFrame.Builder builder) {
- builder.store(localIndex, frameType.join(otherFrameType));
+ builder.store(localIndex, frameType.join(appView, otherFrameType));
}
private void joinWideLocalsWithSameIndex(
@@ -431,7 +450,13 @@
WideFrameType frameType,
WideFrameType otherFrameType,
CfFrame.Builder builder) {
- builder.store(localIndex, frameType.join(otherFrameType));
+ WideFrameType join = frameType.join(otherFrameType);
+ if (join.isPrecise()) {
+ builder.store(localIndex, join);
+ } else {
+ assert join.isTwoWord();
+ setWideLocalToTop(localIndex, builder);
+ }
}
// TODO(b/231521474): By splitting each wide type into single left/right types, the join of each
@@ -496,11 +521,13 @@
private Entry<FrameType> nextLocal(ObjectBidirectionalIterator<Entry<FrameType>> iterator) {
Entry<FrameType> entry = iterator.next();
FrameType frameType = entry.getValue();
- if (frameType.isWide()) {
- assert frameType.isDouble() || frameType.isLong();
+ if (frameType.isWidePrimitive()) {
+ assert frameType.isDoubleLow() || frameType.isLongLow();
Entry<FrameType> highEntry = iterator.next();
assert highEntry.getIntKey() == entry.getIntKey() + 1;
- assert highEntry.getValue() == frameType;
+ assert highEntry.getValue() == frameType.asWidePrimitive().getHighType();
+ } else {
+ assert !frameType.isWide();
}
return entry;
}
@@ -508,11 +535,13 @@
private void previousLocal(ObjectBidirectionalIterator<Entry<FrameType>> iterator) {
Entry<FrameType> entry = iterator.previous();
FrameType frameType = entry.getValue();
- if (frameType.isWide()) {
- assert frameType.isDouble() || frameType.isLong();
+ if (frameType.isWidePrimitive()) {
+ assert frameType.isDoubleHigh() || frameType.isLongHigh();
Entry<FrameType> lowEntry = iterator.previous();
assert lowEntry.getIntKey() == entry.getIntKey() - 1;
- assert lowEntry.getValue() == frameType;
+ assert lowEntry.getValue() == frameType.asWidePrimitive().getLowType();
+ } else {
+ assert !frameType.isWide();
}
}
@@ -536,7 +565,10 @@
setSingleLocalToTop(localIndex + 1, builder);
}
- private ErroneousCfFrameState joinStack(Deque<PreciseFrameType> stack, CfFrame.Builder builder) {
+ private ErroneousCfFrameState joinStack(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Deque<PreciseFrameType> stack,
+ CfFrame.Builder builder) {
Iterator<PreciseFrameType> iterator = this.stack.iterator();
Iterator<PreciseFrameType> otherIterator = stack.iterator();
int stackIndex = 0;
@@ -554,7 +586,7 @@
}
PreciseFrameType preciseJoin;
if (frameType.isSingle()) {
- SingleFrameType join = frameType.asSingle().join(otherFrameType.asSingle());
+ SingleFrameType join = frameType.asSingle().join(appView, otherFrameType.asSingle());
if (join.isOneWord()) {
return joinStackImpreciseJoinError(stackIndex, frameType, otherFrameType);
}
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
index 83cd59d..1274958 100644
--- 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
@@ -25,6 +25,7 @@
private final String message;
ErroneousCfFrameState(String message) {
+ assert message != null;
this.message = message;
}
@@ -114,17 +115,17 @@
}
@Override
- public CfFrameState check(AppView<?> appView, CfFrame frame) {
+ public CfFrameState check(CfAnalysisConfig config, CfFrame frame) {
return this;
}
@Override
- public CfFrameState checkLocals(AppView<?> appView, CfFrame frame) {
+ public CfFrameState checkLocals(CfAnalysisConfig config, CfFrame frame) {
return this;
}
@Override
- public CfFrameState checkStack(AppView<?> appView, CfFrame frame) {
+ public CfFrameState checkStack(CfAnalysisConfig config, CfFrame frame) {
return this;
}
@@ -163,13 +164,15 @@
@Override
public CfFrameState popInitialized(
AppView<?> appView,
+ CfAnalysisConfig config,
DexType expectedType,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
return this;
}
@Override
- public CfFrameState popInitialized(AppView<?> appView, DexType... expectedTypes) {
+ public CfFrameState popInitialized(
+ AppView<?> appView, CfAnalysisConfig config, DexType... expectedTypes) {
return this;
}
@@ -184,8 +187,14 @@
}
@Override
+ public CfFrameState pushException(CfAnalysisConfig config, DexType guard) {
+ return this;
+ }
+
+ @Override
public CfFrameState readLocal(
AppView<?> appView,
+ CfAnalysisConfig config,
int localIndex,
ValueType expectedType,
BiFunction<CfFrameState, FrameType, CfFrameState> fn) {
@@ -199,11 +208,18 @@
@Override
public boolean equals(Object other) {
- return this == other;
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ ErroneousCfFrameState error = (ErroneousCfFrameState) other;
+ return message.equals(error.message);
}
@Override
public int hashCode() {
- return System.identityHashCode(this);
+ return message.hashCode();
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
new file mode 100644
index 0000000..d5a8f6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.retrace.internal.MappingSupplierInternal;
+
+@Keep
+public abstract class MappingSupplier<T extends MappingSupplier<T>>
+ extends MappingSupplierInternal {
+
+ /***
+ * Register an allowed mapping lookup to allow for prefetching of resources.
+ *
+ * @param classReference The minified class reference allowed to be lookup up.
+ */
+ public abstract T registerClassUse(ClassReference classReference);
+
+ public abstract void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingProviderBuilder.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
similarity index 64%
rename from src/main/java/com/android/tools/r8/retrace/MappingProviderBuilder.java
rename to src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
index f9dbd492..c57d88a 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingProviderBuilder.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
@@ -4,18 +4,15 @@
package com.android.tools.r8.retrace;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
@Keep
-public abstract class MappingProviderBuilder<
- P extends MappingProvider, T extends MappingProviderBuilder<P, T>> {
+public abstract class MappingSupplierBuilder<
+ P extends MappingSupplier, T extends MappingSupplierBuilder<P, T>> {
public abstract T self();
public abstract T setAllowExperimental(boolean allowExperimental);
- public abstract T setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler);
-
public abstract P build();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMappingProvider.java b/src/main/java/com/android/tools/r8/retrace/ProguardMappingProvider.java
deleted file mode 100644
index e192003..0000000
--- a/src/main/java/com/android/tools/r8/retrace/ProguardMappingProvider.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace;
-
-import com.android.tools.r8.Keep;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.retrace.internal.ProguardMappingProviderBuilderImpl;
-
-@Keep
-public abstract class ProguardMappingProvider extends MappingProvider {
-
- public static Builder builder() {
- return new ProguardMappingProviderBuilderImpl();
- }
-
- @Keep
- public abstract static class Builder
- extends MappingProviderBuilder<ProguardMappingProvider, Builder> {
-
- public abstract Builder registerUse(ClassReference classReference);
-
- public abstract Builder allowLookupAllClasses();
-
- public abstract Builder setProguardMapProducer(ProguardMapProducer proguardMapProducer);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.java
new file mode 100644
index 0000000..74cd98f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMappingSupplier.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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.ProguardMappingSupplierBuilderImpl;
+
+@Keep
+public abstract class ProguardMappingSupplier extends MappingSupplier<ProguardMappingSupplier> {
+
+ public static Builder builder() {
+ return new ProguardMappingSupplierBuilderImpl();
+ }
+
+ @Keep
+ public abstract static class Builder
+ extends MappingSupplierBuilder<ProguardMappingSupplier, Builder> {
+
+ public abstract Builder setProguardMapProducer(ProguardMapProducer proguardMapProducer);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingProvider.java b/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
similarity index 67%
rename from src/main/java/com/android/tools/r8/retrace/MappingProvider.java
rename to src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
index 18bf9ee..03988a9 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingProvider.java
+++ b/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
@@ -5,7 +5,11 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.MappingProviderInternal;
@Keep
-public abstract class MappingProvider extends MappingProviderInternal {}
+public interface ResultWithContext<T> {
+
+ RetraceStackTraceContext getContext();
+
+ T getResult();
+}
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 890e4d3..bb1bd0e 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,11 +10,10 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.Version;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker.VerifyMappingFileHashResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceCommand.Builder;
+import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
@@ -31,9 +30,7 @@
import com.google.common.base.Charsets;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.io.CharStreams;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
@@ -110,7 +107,7 @@
continue;
}
if (!hasSetProguardMap) {
- builder.setProguardMapProducer(getMappingSupplier(context.head(), diagnosticsHandler));
+ builder.setMappingSupplier(getMappingSupplier(context.head(), diagnosticsHandler));
context.next();
hasSetProguardMap = true;
} else if (!hasSetStackTrace) {
@@ -135,7 +132,7 @@
return builder;
}
- private static ProguardMapProducer getMappingSupplier(
+ private static MappingSupplier getMappingSupplier(
String mappingPath, DiagnosticsHandler diagnosticsHandler) {
Path path = Paths.get(mappingPath);
if (!Files.exists(path)) {
@@ -143,7 +140,12 @@
new StringDiagnostic(String.format("Could not find mapping file '%s'.", mappingPath)));
throw new RetraceAbortException();
}
- return ProguardMapProducer.fromPath(Paths.get(mappingPath));
+ boolean allowExperimentalMapVersion =
+ System.getProperty("com.android.tools.r8.experimentalmapping") != null;
+ return ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromPath(Paths.get(mappingPath)))
+ .setAllowExperimental(allowExperimentalMapVersion)
+ .build();
}
private static List<String> getStackTraceFromFile(
@@ -176,9 +178,11 @@
* Retraces a complete stack frame and returns a list of retraced stack traces.
*
* @param stackTrace the stack trace to be retrace
+ * @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public List<List<List<T>>> retraceStackTrace(List<T> stackTrace) {
+ public ResultWithContext<List<List<List<T>>>> retraceStackTrace(
+ List<T> stackTrace, RetraceStackTraceContext context) {
ListUtils.forEachWithIndex(
stackTrace,
(line, lineNumber) -> {
@@ -189,69 +193,75 @@
}
});
List<ST> parsed = ListUtils.map(stackTrace, stackTraceLineParser::parse);
- return retraceStackTraceParsed(parsed);
+ return retraceStackTraceParsed(parsed, context);
}
/**
* Retraces a complete stack frame and returns a list of retraced stack traces.
*
* @param stackTrace the stack trace to be retrace
+ * @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public List<List<List<T>>> retraceStackTraceParsed(List<ST> stackTrace) {
+ public ResultWithContext<List<List<List<T>>>> retraceStackTraceParsed(
+ List<ST> stackTrace, RetraceStackTraceContext context) {
RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
List<List<List<T>>> finalResult = new ArrayList<>();
- ListUtils.fold(
- stackTrace,
- RetraceStackTraceContext.empty(),
- (context, stackTraceLine) -> {
- List<Pair<RetraceStackTraceElementProxy<T, ST>, List<T>>> resultsForLine =
- new ArrayList<>();
- Box<List<T>> currentList = new Box<>();
- Set<Wrapper<RetraceStackTraceElementProxy<T, ST>>> seen = new HashSet<>();
- List<RetraceStackTraceContext> contexts = new ArrayList<>();
- RetraceStackTraceElementProxyResult<T, ST> retraceResult =
- proxyRetracer.retrace(stackTraceLine, context);
- retraceResult.stream()
- .forEach(
- retracedElement -> {
- if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
- if (seen.add(equivalence.wrap(retracedElement))) {
- currentList.set(new ArrayList<>());
- resultsForLine.add(Pair.create(retracedElement, currentList.get()));
- contexts.add(retracedElement.getContext());
- } else {
- currentList.clear();
- }
- }
- if (currentList.isSet()) {
- currentList
- .get()
- .add(stackTraceLine.toRetracedItem(retracedElement, isVerbose));
- }
- });
- resultsForLine.sort(Comparator.comparing(Pair::getFirst));
- finalResult.add(ListUtils.map(resultsForLine, Pair::getSecond));
- if (contexts.isEmpty()) {
- return retraceResult.getResultContext();
- }
- return contexts.size() == 1 ? contexts.get(0) : RetraceStackTraceContext.empty();
- });
- return finalResult;
+ RetraceStackTraceContext finalContext =
+ ListUtils.fold(
+ stackTrace,
+ context,
+ (newContext, stackTraceLine) -> {
+ List<Pair<RetraceStackTraceElementProxy<T, ST>, List<T>>> resultsForLine =
+ new ArrayList<>();
+ Box<List<T>> currentList = new Box<>();
+ Set<Wrapper<RetraceStackTraceElementProxy<T, ST>>> seen = new HashSet<>();
+ List<RetraceStackTraceContext> contexts = new ArrayList<>();
+ RetraceStackTraceElementProxyResult<T, ST> retraceResult =
+ proxyRetracer.retrace(stackTraceLine, newContext);
+ retraceResult.stream()
+ .forEach(
+ retracedElement -> {
+ if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+ if (seen.add(equivalence.wrap(retracedElement))) {
+ currentList.set(new ArrayList<>());
+ resultsForLine.add(Pair.create(retracedElement, currentList.get()));
+ contexts.add(retracedElement.getContext());
+ } else {
+ currentList.clear();
+ }
+ }
+ if (currentList.isSet()) {
+ currentList
+ .get()
+ .add(stackTraceLine.toRetracedItem(retracedElement, isVerbose));
+ }
+ });
+ resultsForLine.sort(Comparator.comparing(Pair::getFirst));
+ finalResult.add(ListUtils.map(resultsForLine, Pair::getSecond));
+ if (contexts.isEmpty()) {
+ return retraceResult.getResultContext();
+ }
+ return contexts.size() == 1 ? contexts.get(0) : RetraceStackTraceContext.empty();
+ });
+ return ResultWithContextImpl.create(finalResult, finalContext);
}
/**
* Retraces a stack trace frame with support for splitting up ambiguous results.
*
* @param stackTraceFrame The frame to retrace that can give rise to ambiguous results
+ * @param context The context to retrace the stack trace in
* @return A collection of retraced frame where each entry in the outer list is ambiguous
*/
- public List<List<T>> retraceFrame(T stackTraceFrame) {
+ public ResultWithContext<List<List<T>>> retraceFrame(
+ T stackTraceFrame, RetraceStackTraceContext context) {
Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
ST parsedLine = stackTraceLineParser.parse(stackTraceFrame);
- proxyRetracer.retrace(parsedLine, RetraceStackTraceContext.empty()).stream()
+ Box<RetraceStackTraceContext> contextBox = new Box<>(context);
+ proxyRetracer.retrace(parsedLine, context).stream()
.forEach(
retracedElement -> {
if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
@@ -261,11 +271,12 @@
ambiguousBlocks
.get(ListUtils.last(ambiguousKeys))
.add(parsedLine.toRetracedItem(retracedElement, isVerbose));
+ contextBox.set(retracedElement.getContext());
});
Collections.sort(ambiguousKeys);
List<List<T>> retracedList = new ArrayList<>();
ambiguousKeys.forEach(key -> retracedList.add(ambiguousBlocks.get(key)));
- return retracedList;
+ return ResultWithContextImpl.create(retracedList, contextBox.get());
}
/**
@@ -273,17 +284,22 @@
* to distinguish them. For retracing with ambiguous results separated, use {@link #retraceFrame}
*
* @param stackTraceLine the stack trace line to retrace
+ * @param context The context to retrace the stack trace in
* @return the retraced stack trace line
*/
- public List<T> retraceLine(T stackTraceLine) {
+ public ResultWithContext<List<T>> retraceLine(
+ T stackTraceLine, RetraceStackTraceContext context) {
ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
- return proxyRetracer.retrace(parsedLine, RetraceStackTraceContext.empty()).stream()
- .map(
- retraceFrame -> {
- retraceFrame.getOriginalItem().toRetracedItem(retraceFrame, isVerbose);
- return parsedLine.toRetracedItem(retraceFrame, isVerbose);
- })
- .collect(Collectors.toList());
+ Box<RetraceStackTraceContext> contextBox = new Box<>(context);
+ List<T> result =
+ proxyRetracer.retrace(parsedLine, context).stream()
+ .map(
+ retraceFrame -> {
+ contextBox.set(retraceFrame.getContext());
+ return parsedLine.toRetracedItem(retraceFrame, isVerbose);
+ })
+ .collect(Collectors.toList());
+ return ResultWithContextImpl.create(result, contextBox.get());
}
/**
@@ -292,80 +308,21 @@
* @param command The command that describes the desired behavior of this retrace invocation.
*/
public static void run(RetraceCommand command) {
- boolean allowExperimentalMapVersion =
- System.getProperty("com.android.tools.r8.experimentalmapping") != null;
- runForTesting(command, allowExperimentalMapVersion);
- }
-
- static void runForTesting(RetraceCommand command, boolean allowExperimentalMapping) {
try {
Timing timing = Timing.create("R8 retrace", command.printMemory());
RetraceOptions options = command.getOptions();
+ MappingSupplier<?> mappingSupplier = options.getMappingSupplier();
if (command.getOptions().isVerifyMappingFileHash()) {
- try (InputStream reader = options.getProguardMapProducer().get()) {
- VerifyMappingFileHashResult checkResult =
- ProguardMapChecker.validateProguardMapHash(
- CharStreams.toString(new InputStreamReader(reader, Charsets.UTF_8)));
- if (checkResult.isError()) {
- command
- .getOptions()
- .getDiagnosticsHandler()
- .error(new StringDiagnostic(checkResult.getMessage()));
- throw new RuntimeException(checkResult.getMessage());
- }
- if (!checkResult.isOk()) {
- command
- .getOptions()
- .getDiagnosticsHandler()
- .warning(new StringDiagnostic(checkResult.getMessage()));
- }
- } catch (IOException e) {
- command.getOptions().getDiagnosticsHandler().error(new ExceptionDiagnostic(e));
- throw new RuntimeException(e);
- }
+ mappingSupplier.verifyMappingFileHash(options.getDiagnosticsHandler());
return;
}
DiagnosticsHandler diagnosticsHandler = options.getDiagnosticsHandler();
- timing.begin("Parsing");
StackTraceRegularExpressionParser stackTraceLineParser =
new StackTraceRegularExpressionParser(options.getRegularExpression());
- List<StackTraceElementStringProxy> parsedStackTrace = new ArrayList<>();
- ListUtils.forEachWithIndex(
- command.getStackTrace(),
- (line, lineNumber) -> {
- if (line == null) {
- diagnosticsHandler.error(
- RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
- throw new RetraceAbortException();
- }
- parsedStackTrace.add(stackTraceLineParser.parse(line));
- });
- timing.end();
timing.begin("Read proguard map");
- ProguardMappingProvider.Builder mappingBuilder =
- ProguardMappingProvider.builder()
- .setProguardMapProducer(options.getProguardMapProducer())
- .setDiagnosticsHandler(diagnosticsHandler)
- .setAllowExperimental(allowExperimentalMapping);
- parsedStackTrace.forEach(
- proxy -> {
- if (proxy.hasClassName()) {
- mappingBuilder.registerUse(proxy.getClassReference());
- }
- if (proxy.hasMethodArguments()) {
- Arrays.stream(proxy.getMethodArguments().split(","))
- .forEach(typeName -> registerUseFromTypeReference(mappingBuilder, typeName));
- }
- if (proxy.hasFieldOrReturnType() && !proxy.getFieldOrReturnType().equals("void")) {
- registerUseFromTypeReference(mappingBuilder, proxy.getFieldOrReturnType());
- }
- });
- MappingProvider mappingProvider = mappingBuilder.build();
- timing.end();
- timing.begin("Retracing");
RetracerImpl retracer =
RetracerImpl.builder()
- .setMappingProvider(mappingProvider)
+ .setMappingSupplier(mappingSupplier)
.setDiagnosticsHandler(diagnosticsHandler)
.build();
retracer
@@ -377,17 +334,55 @@
RetraceUnknownMapVersionDiagnostic.create(mapVersionInfo.getValue()));
}
});
- StringRetrace stringRetrace =
+ StringRetrace stringRetracer =
new StringRetrace(
stackTraceLineParser,
StackTraceElementProxyRetracer.createDefault(retracer),
diagnosticsHandler,
options.isVerbose());
- List<String> retraced = stringRetrace.retraceParsed(parsedStackTrace);
timing.end();
- timing.begin("Report result");
- command.getRetracedStackTraceConsumer().accept(retraced);
- timing.end();
+ StackTraceSupplier stackTraceSupplier = command.getStacktraceSupplier();
+ int lineNumber = 0;
+ RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+ List<String> currentStackTrace;
+ while ((currentStackTrace = stackTraceSupplier.get()) != null) {
+ timing.begin("Parsing");
+ List<StackTraceElementStringProxy> parsedStackTrace = new ArrayList<>();
+ for (String line : currentStackTrace) {
+ if (line == null) {
+ diagnosticsHandler.error(
+ RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
+ throw new RetraceAbortException();
+ }
+ parsedStackTrace.add(stackTraceLineParser.parse(line));
+ lineNumber += 1;
+ }
+ timing.end();
+ parsedStackTrace.forEach(
+ proxy -> {
+ if (proxy.hasClassName()) {
+ mappingSupplier.registerClassUse(proxy.getClassReference());
+ }
+ if (proxy.hasMethodArguments()) {
+ Arrays.stream(proxy.getMethodArguments().split(","))
+ .forEach(typeName -> registerUseFromTypeReference(mappingSupplier, typeName));
+ }
+ if (proxy.hasFieldOrReturnType() && !proxy.getFieldOrReturnType().equals("void")) {
+ registerUseFromTypeReference(mappingSupplier, proxy.getFieldOrReturnType());
+ }
+ });
+ timing.begin("Retracing");
+ ResultWithContext<List<String>> listResultWithContext =
+ stringRetracer.retraceParsed(parsedStackTrace, context);
+ timing.end();
+ timing.begin("Report result");
+ context = listResultWithContext.getContext();
+ List<String> result = listResultWithContext.getResult();
+ if (!result.isEmpty() || currentStackTrace.isEmpty()) {
+ command.getRetracedStackTraceConsumer().accept(result);
+ }
+ timing.end();
+ }
if (command.printTimes()) {
timing.report();
}
@@ -398,13 +393,13 @@
}
private static void registerUseFromTypeReference(
- ProguardMappingProvider.Builder builder, String typeName) {
+ MappingSupplier<?> mappingSupplier, String typeName) {
TypeReference typeReference = Reference.typeFromTypeName(typeName);
if (typeReference.isArray()) {
typeReference = typeReference.asArray().getBaseType();
}
if (typeReference.isClass()) {
- builder.registerUse(typeReference.asClass());
+ mappingSupplier.registerClassUse(typeReference.asClass());
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index b6d70e9..10db02a 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -7,26 +7,27 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
+import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.function.Consumer;
@Keep
public class RetraceCommand {
- private final List<String> stackTrace;
+ private final StackTraceSupplier stacktraceSupplier;
private final Consumer<List<String>> retracedStackTraceConsumer;
// Not inheriting to allow for static builder methods.
private final RetraceOptions options;
private RetraceCommand(
- List<String> stackTrace,
+ StackTraceSupplier stackTraceSupplier,
Consumer<List<String>> retracedStackTraceConsumer,
RetraceOptions options) {
- this.stackTrace = stackTrace;
+ this.stacktraceSupplier = stackTraceSupplier;
this.retracedStackTraceConsumer = retracedStackTraceConsumer;
this.options = options;
- assert this.stackTrace != null || options.isVerifyMappingFileHash();
+ assert this.stacktraceSupplier != null || options.isVerifyMappingFileHash();
assert this.retracedStackTraceConsumer != null;
}
@@ -38,8 +39,8 @@
return System.getProperty("com.android.tools.r8.printmemory") != null;
}
- public List<String> getStackTrace() {
- return stackTrace;
+ public StackTraceSupplier getStacktraceSupplier() {
+ return stacktraceSupplier;
}
public Consumer<List<String>> getRetracedStackTraceConsumer() {
@@ -69,9 +70,9 @@
private boolean isVerbose;
private final DiagnosticsHandler diagnosticsHandler;
- private ProguardMapProducer proguardMapProducer;
+ private MappingSupplier<?> mappingSupplier;
private String regularExpression = StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
- private List<String> stackTrace;
+ private StackTraceSupplier stackTrace;
private Consumer<List<String>> retracedStackTraceConsumer;
private boolean verifyMappingFileHash = false;
@@ -85,13 +86,9 @@
return this;
}
- /**
- * Set a producer for the proguard mapping contents.
- *
- * @param producer Producer for
- */
- public Builder setProguardMapProducer(ProguardMapProducer producer) {
- this.proguardMapProducer = producer;
+ /** Set a mapping supplier for providing mapping contents. */
+ public Builder setMappingSupplier(MappingSupplier<?> mappingSupplier) {
+ this.mappingSupplier = mappingSupplier;
return this;
}
@@ -114,6 +111,17 @@
* first line.
*/
public Builder setStackTrace(List<String> stackTrace) {
+ Box<List<String>> box = new Box<>(stackTrace);
+ return setStackTrace(() -> box.getAndSet(null));
+ }
+
+ /**
+ * Set a supplier of the obfuscated stack trace that is to be retraced.
+ *
+ * @param stackTrace Supplier where the first query should provide the top-most entry(the
+ * closest stack to the error). Use null to specify no more lines.
+ */
+ public Builder setStackTrace(StackTraceSupplier stackTrace) {
this.stackTrace = stackTrace;
return this;
}
@@ -138,7 +146,7 @@
if (this.diagnosticsHandler == null) {
throw new RuntimeException("DiagnosticsHandler not specified");
}
- if (this.proguardMapProducer == null) {
+ if (this.mappingSupplier == null) {
throw new RuntimeException("ProguardMapSupplier not specified");
}
if (this.stackTrace == null && !verifyMappingFileHash) {
@@ -150,7 +158,7 @@
RetraceOptions retraceOptions =
RetraceOptions.builder(diagnosticsHandler)
.setRegularExpression(regularExpression)
- .setProguardMapProducer(proguardMapProducer)
+ .setMappingSupplier(mappingSupplier)
.setVerbose(isVerbose)
.setVerifyMappingFileHash(verifyMappingFileHash)
.build();
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java b/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java
deleted file mode 100644
index c966897..0000000
--- a/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.retrace;
-
-/** Non-kept class for internal access from tests. */
-public class RetraceHelper {
-
- public static void runForTesting(RetraceCommand command, boolean allowExperimentalMapping) {
- Retrace.runForTesting(command, allowExperimentalMapping);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
index 2543f35..7c00ed2 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
@@ -20,22 +20,22 @@
private final boolean verifyMappingFileHash;
private final String regularExpression;
private final DiagnosticsHandler diagnosticsHandler;
- private final ProguardMapProducer proguardMapProducer;
+ private final MappingSupplier mappingSupplier;
private RetraceOptions(
String regularExpression,
DiagnosticsHandler diagnosticsHandler,
- ProguardMapProducer proguardMapProducer,
+ MappingSupplier mappingSupplier,
boolean isVerbose,
boolean verifyMappingFileHash) {
this.regularExpression = regularExpression;
this.diagnosticsHandler = diagnosticsHandler;
- this.proguardMapProducer = proguardMapProducer;
+ this.mappingSupplier = mappingSupplier;
this.isVerbose = isVerbose;
this.verifyMappingFileHash = verifyMappingFileHash;
assert diagnosticsHandler != null;
- assert proguardMapProducer != null;
+ assert mappingSupplier != null;
}
public boolean isVerbose() {
@@ -54,8 +54,8 @@
return diagnosticsHandler;
}
- public ProguardMapProducer getProguardMapProducer() {
- return proguardMapProducer;
+ public MappingSupplier getMappingSupplier() {
+ return mappingSupplier;
}
/** Utility method for obtaining a builder with a default diagnostics handler. */
@@ -78,7 +78,7 @@
private boolean isVerbose;
private boolean verifyMappingFileHash;
private final DiagnosticsHandler diagnosticsHandler;
- private ProguardMapProducer proguardMapProducer;
+ private MappingSupplier mappingSupplier;
private String regularExpression = defaultRegularExpression();
Builder(DiagnosticsHandler diagnosticsHandler) {
@@ -97,13 +97,9 @@
return this;
}
- /**
- * Set a producer for the proguard mapping contents.
- *
- * @param producer Producer for
- */
- public Builder setProguardMapProducer(ProguardMapProducer producer) {
- this.proguardMapProducer = producer;
+ /** Set a mapping supplier for providing mapping contents. */
+ public Builder setMappingSupplier(MappingSupplier producer) {
+ this.mappingSupplier = producer;
return this;
}
@@ -123,18 +119,14 @@
if (this.diagnosticsHandler == null) {
throw new RuntimeException("DiagnosticsHandler not specified");
}
- if (this.proguardMapProducer == null) {
+ if (this.mappingSupplier == null) {
throw new RuntimeException("ProguardMapSupplier not specified");
}
if (this.regularExpression == null) {
throw new RuntimeException("Regular expression not specified");
}
return new RetraceOptions(
- regularExpression,
- diagnosticsHandler,
- proguardMapProducer,
- isVerbose,
- verifyMappingFileHash);
+ regularExpression, diagnosticsHandler, mappingSupplier, isVerbose, verifyMappingFileHash);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 15d3b48..650883b 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -73,14 +73,10 @@
static Retracer createDefault(
ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
try {
- ProguardMappingProvider mappingProvider =
- ProguardMappingProvider.builder()
- .setProguardMapProducer(proguardMapProducer)
- .setDiagnosticsHandler(diagnosticsHandler)
- .allowLookupAllClasses()
- .build();
+ ProguardMappingSupplier mappingSupplier =
+ ProguardMappingSupplier.builder().setProguardMapProducer(proguardMapProducer).build();
return Retracer.builder()
- .setMappingProvider(mappingProvider)
+ .setMappingSupplier(mappingSupplier)
.setDiagnosticsHandler(diagnosticsHandler)
.build();
} catch (Exception e) {
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java b/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java
index efd56ce..21eb8e0 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracerBuilder.java
@@ -10,7 +10,7 @@
@Keep
public interface RetracerBuilder {
- RetracerBuilder setMappingProvider(MappingProvider mappingProvider);
+ RetracerBuilder setMappingSupplier(MappingSupplier mappingSupplier);
RetracerBuilder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingProvider.java b/src/main/java/com/android/tools/r8/retrace/StackTraceSupplier.java
similarity index 67%
copy from src/main/java/com/android/tools/r8/retrace/MappingProvider.java
copy to src/main/java/com/android/tools/r8/retrace/StackTraceSupplier.java
index 18bf9ee..9940c5c 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingProvider.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceSupplier.java
@@ -5,7 +5,11 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.MappingProviderInternal;
+import java.util.List;
@Keep
-public abstract class MappingProvider extends MappingProviderInternal {}
+@FunctionalInterface
+public interface StackTraceSupplier {
+
+ List<String> get();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index b5ed6ae..1c1a2ee 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
+import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -40,7 +42,10 @@
*/
public static StringRetrace create(RetraceOptions command) {
return create(
- Retracer.createDefault(command.getProguardMapProducer(), command.getDiagnosticsHandler()),
+ RetracerImpl.builder()
+ .setMappingSupplier(command.getMappingSupplier())
+ .setDiagnosticsHandler(command.getDiagnosticsHandler())
+ .build(),
command.getDiagnosticsHandler(),
command.getRegularExpression(),
command.isVerbose());
@@ -73,12 +78,15 @@
* appended automatically to the retraced string.
*
* @param stackTrace the incoming stack trace
+ * @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public List<String> retrace(List<String> stackTrace) {
+ public ResultWithContext<List<String>> retrace(
+ List<String> stackTrace, RetraceStackTraceContext context) {
+ ResultWithContext<List<List<List<String>>>> listResultWithContext =
+ retraceStackTrace(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- List<List<List<String>>> lists = retraceStackTrace(stackTrace);
- for (List<List<String>> newLines : lists) {
+ for (List<List<String>> newLines : listResultWithContext.getResult()) {
ListUtils.forEachWithIndex(
newLines,
(inlineFrames, ambiguousIndex) -> {
@@ -102,7 +110,7 @@
}
});
}
- return retracedStrings;
+ return ResultWithContextImpl.create(retracedStrings, listResultWithContext.getContext());
}
/**
@@ -110,12 +118,15 @@
* will be appended automatically to the retraced string.
*
* @param stackTrace the incoming parsed stack trace
+ * @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public List<String> retraceParsed(List<StackTraceElementStringProxy> stackTrace) {
+ public ResultWithContext<List<String>> retraceParsed(
+ List<StackTraceElementStringProxy> stackTrace, RetraceStackTraceContext context) {
+ ResultWithContext<List<List<List<String>>>> listResultWithContext =
+ retraceStackTraceParsed(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- List<List<List<String>>> lists = retraceStackTraceParsed(stackTrace);
- for (List<List<String>> newLines : lists) {
+ for (List<List<String>> newLines : listResultWithContext.getResult()) {
ListUtils.forEachWithIndex(
newLines,
(inlineFrames, ambiguousIndex) -> {
@@ -139,19 +150,23 @@
}
});
}
- return retracedStrings;
+ return ResultWithContextImpl.create(retracedStrings, listResultWithContext.getContext());
}
/**
* Retraces a single stack trace line and returns the potential list of original frames
*
* @param stackTraceLine the stack trace line to retrace
+ * @param context The context to retrace the stack trace in
* @return the retraced frames
*/
- public List<String> retrace(String stackTraceLine) {
+ public ResultWithContext<List<String>> retrace(
+ String stackTraceLine, RetraceStackTraceContext context) {
+ ResultWithContext<List<List<String>>> listResultWithContext =
+ retraceFrame(stackTraceLine, context);
List<String> result = new ArrayList<>();
- joinAmbiguousLines(retraceFrame(stackTraceLine), result::add);
- return result;
+ joinAmbiguousLines(listResultWithContext.getResult(), result::add);
+ return ResultWithContextImpl.create(result, listResultWithContext.getContext());
}
private void joinAmbiguousLines(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingProviderInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingProviderInternal.java
deleted file mode 100644
index 13187cc..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingProviderInternal.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace.internal;
-
-import com.android.tools.r8.naming.ClassNamingForNameMapper;
-import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
-import java.util.Set;
-
-public abstract class MappingProviderInternal {
-
- abstract ClassNamingForNameMapper getClassNaming(String typeName);
-
- abstract String getSourceFileForClass(String typeName);
-
- abstract Set<MapVersionMappingInformation> getMapVersions();
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java
new file mode 100644
index 0000000..c47c467
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingSupplierInternal.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import java.util.Set;
+
+public abstract class MappingSupplierInternal {
+
+ abstract ClassNamingForNameMapper getClassNaming(
+ DiagnosticsHandler diagnosticsHandler, String typeName);
+
+ abstract String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName);
+
+ abstract Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler);
+}
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 0d764a5..e6589d5f 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
@@ -189,9 +189,12 @@
private int endIndex = 0;
private final Predicate<String> filter;
+ private final boolean readPreambleAndSourceFiles;
- protected ProguardMapReaderWithFiltering(Predicate<String> filter) {
+ protected ProguardMapReaderWithFiltering(
+ Predicate<String> filter, boolean readPreambleAndSourceFiles) {
this.filter = filter;
+ this.readPreambleAndSourceFiles = readPreambleAndSourceFiles;
}
public abstract byte[] read() throws IOException;
@@ -221,10 +224,12 @@
String classMapping = getBufferAsString(bytes);
String obfuscatedClassName = getObfuscatedClassName(classMapping);
isInsideClassOfInterest = filter.test(obfuscatedClassName);
- return classMapping;
- } else if (lineParserState == IS_COMMENT_SOURCE_FILE) {
+ if (isInsideClassOfInterest || readPreambleAndSourceFiles) {
+ return classMapping;
+ }
+ } else if (lineParserState == IS_COMMENT_SOURCE_FILE && readPreambleAndSourceFiles) {
return getBufferAsString(bytes);
- } else if (isInsideClassOfInterest || !seenFirstClass) {
+ } else if (isInsideClassOfInterest || (!seenFirstClass && readPreambleAndSourceFiles)) {
return getBufferAsString(bytes);
}
}
@@ -284,8 +289,11 @@
private int temporaryBufferPosition = 0;
public ProguardMapReaderWithFilteringMappedBuffer(
- Path mappingFile, Predicate<String> classNamesOfInterest) throws IOException {
- super(classNamesOfInterest);
+ Path mappingFile,
+ Predicate<String> classNamesOfInterest,
+ boolean readPreambleAndSourceFiles)
+ throws IOException {
+ super(classNamesOfInterest, readPreambleAndSourceFiles);
fileChannel = FileChannel.open(mappingFile, StandardOpenOption.READ);
channelSize = fileChannel.size();
readFromChannel();
@@ -364,8 +372,10 @@
private int endReadIndex = 0;
public ProguardMapReaderWithFilteringInputBuffer(
- InputStream inputStream, Predicate<String> classNamesOfInterest) {
- super(classNamesOfInterest);
+ InputStream inputStream,
+ Predicate<String> classNamesOfInterest,
+ boolean readPreambleAndSourceFiles) {
+ super(classNamesOfInterest, readPreambleAndSourceFiles);
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
deleted file mode 100644
index 8cf9ace..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderBuilderImpl.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace.internal;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.LineReader;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.retrace.InvalidMappingFileException;
-import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
-import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
-import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringMappedBuffer;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.function.Predicate;
-
-public class ProguardMappingProviderBuilderImpl extends ProguardMappingProvider.Builder {
-
- private ProguardMapProducer proguardMapProducer;
- private boolean allowExperimental = false;
- private Set<String> allowedLookup = new HashSet<>();
- private boolean allowLookupAllClasses = false;
- private DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
-
- @Override
- public ProguardMappingProvider.Builder self() {
- return this;
- }
-
- @Override
- public ProguardMappingProvider.Builder setAllowExperimental(boolean allowExperimental) {
- this.allowExperimental = allowExperimental;
- return self();
- }
-
- @Override
- public ProguardMappingProvider.Builder setDiagnosticsHandler(
- DiagnosticsHandler diagnosticsHandler) {
- this.diagnosticsHandler = diagnosticsHandler;
- return self();
- }
-
- @Override
- public ProguardMappingProvider.Builder registerUse(ClassReference classReference) {
- allowedLookup.add(classReference.getTypeName());
- return self();
- }
-
- @Override
- public ProguardMappingProvider.Builder allowLookupAllClasses() {
- allowLookupAllClasses = true;
- return self();
- }
-
- @Override
- public ProguardMappingProvider.Builder setProguardMapProducer(
- ProguardMapProducer proguardMapProducer) {
- this.proguardMapProducer = proguardMapProducer;
- return self();
- }
-
- @Override
- public ProguardMappingProvider build() {
- try {
- Predicate<String> buildForClass = allowLookupAllClasses ? null : allowedLookup::contains;
- LineReader reader =
- proguardMapProducer.isFileBacked()
- ? new ProguardMapReaderWithFilteringMappedBuffer(
- proguardMapProducer.getPath(), buildForClass)
- : new ProguardMapReaderWithFilteringInputBuffer(
- proguardMapProducer.get(), buildForClass);
- return new ProguardMappingProviderImpl(
- ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader, diagnosticsHandler, true, allowExperimental));
- } catch (Exception e) {
- throw new InvalidMappingFileException(e);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderImpl.java
deleted file mode 100644
index c15efef..0000000
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingProviderImpl.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.retrace.internal;
-
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNamingForNameMapper;
-import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
-import com.android.tools.r8.retrace.IllegalClassNameLookupException;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
-import java.util.Set;
-
-/**
- * IntelliJ highlights the class as being invalid because it cannot see getClassNameMapper is
- * defined on the class for some reason.
- */
-public class ProguardMappingProviderImpl extends ProguardMappingProvider {
-
- private final ClassNameMapper classNameMapper;
- private final Set<String> allowedLookupTypeNames;
-
- public ProguardMappingProviderImpl(ClassNameMapper classNameMapper) {
- this(classNameMapper, null);
- }
-
- public ProguardMappingProviderImpl(
- ClassNameMapper classNameMapper, Set<String> allowedLookupTypeNames) {
- this.classNameMapper = classNameMapper;
- this.allowedLookupTypeNames = allowedLookupTypeNames;
- }
-
- @Override
- Set<MapVersionMappingInformation> getMapVersions() {
- return classNameMapper.getMapVersions();
- }
-
- @Override
- ClassNamingForNameMapper getClassNaming(String typeName) {
- // TODO(b/226885646): Enable lookup check when there are no additional lookups.
- if (false && !allowedLookupTypeNames.contains(typeName)) {
- throw new IllegalClassNameLookupException(typeName);
- }
- return classNameMapper.getClassNaming(typeName);
- }
-
- @Override
- String getSourceFileForClass(String typeName) {
- return classNameMapper.getSourceFile(typeName);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java
new file mode 100644
index 0000000..8cea599
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierBuilderImpl.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+
+public class ProguardMappingSupplierBuilderImpl extends ProguardMappingSupplier.Builder {
+
+ private ProguardMapProducer proguardMapProducer;
+ private boolean allowExperimental = false;
+
+ @Override
+ public ProguardMappingSupplier.Builder self() {
+ return this;
+ }
+
+ @Override
+ public ProguardMappingSupplier.Builder setAllowExperimental(boolean allowExperimental) {
+ this.allowExperimental = allowExperimental;
+ return self();
+ }
+
+ @Override
+ public ProguardMappingSupplier.Builder setProguardMapProducer(
+ ProguardMapProducer proguardMapProducer) {
+ this.proguardMapProducer = proguardMapProducer;
+ return self();
+ }
+
+ @Override
+ public ProguardMappingSupplier build() {
+ return new ProguardMappingSupplierImpl(proguardMapProducer, allowExperimental);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
new file mode 100644
index 0000000..a6ae27b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.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.retrace.internal;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNamingForNameMapper;
+import com.android.tools.r8.naming.LineReader;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker;
+import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker.VerifyMappingFileHashResult;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.retrace.InvalidMappingFileException;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringMappedBuffer;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * IntelliJ highlights the class as being invalid because it cannot see getClassNameMapper is
+ * defined on the class for some reason.
+ */
+public class ProguardMappingSupplierImpl extends ProguardMappingSupplier {
+
+ private final ProguardMapProducer proguardMapProducer;
+ private final boolean allowExperimental;
+
+ private ClassNameMapper classNameMapper;
+ private final Set<String> pendingClassMappings = new HashSet<>();
+ private final Set<String> builtClassMappings;
+
+ public ProguardMappingSupplierImpl(ClassNameMapper classNameMapper) {
+ this.classNameMapper = classNameMapper;
+ this.proguardMapProducer = null;
+ this.allowExperimental = true;
+ builtClassMappings = null;
+ }
+
+ ProguardMappingSupplierImpl(ProguardMapProducer proguardMapProducer, boolean allowExperimental) {
+ this.proguardMapProducer = proguardMapProducer;
+ this.allowExperimental = allowExperimental;
+ builtClassMappings = proguardMapProducer.isFileBacked() ? new HashSet<>() : null;
+ }
+
+ private boolean hasClassMappingFor(String typeName) {
+ return builtClassMappings == null || builtClassMappings.contains(typeName);
+ }
+
+ @Override
+ Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
+ return getClassNameMapper(diagnosticsHandler).getMapVersions();
+ }
+
+ @Override
+ ClassNamingForNameMapper getClassNaming(DiagnosticsHandler diagnosticsHandler, String typeName) {
+ if (!hasClassMappingFor(typeName)) {
+ pendingClassMappings.add(typeName);
+ }
+ return getClassNameMapper(diagnosticsHandler).getClassNaming(typeName);
+ }
+
+ @Override
+ String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName) {
+ return getClassNameMapper(diagnosticsHandler).getSourceFile(typeName);
+ }
+
+ private ClassNameMapper getClassNameMapper(DiagnosticsHandler diagnosticsHandler) {
+ if (classNameMapper != null && pendingClassMappings.isEmpty()) {
+ return classNameMapper;
+ }
+ if (classNameMapper != null && !proguardMapProducer.isFileBacked()) {
+ throw new RuntimeException("Cannot re-open a proguard map producer that is not file backed");
+ }
+ try {
+ Predicate<String> buildForClass =
+ builtClassMappings == null ? null : pendingClassMappings::contains;
+ boolean readPreambleAndSourceFile = classNameMapper == null;
+ LineReader reader =
+ proguardMapProducer.isFileBacked()
+ ? new ProguardMapReaderWithFilteringMappedBuffer(
+ proguardMapProducer.getPath(), buildForClass, readPreambleAndSourceFile)
+ : new ProguardMapReaderWithFilteringInputBuffer(
+ proguardMapProducer.get(), buildForClass, readPreambleAndSourceFile);
+ ClassNameMapper classNameMapper =
+ ClassNameMapper.mapperFromLineReaderWithFiltering(
+ reader, getMapVersion(), diagnosticsHandler, true, allowExperimental);
+ this.classNameMapper = classNameMapper.combine(this.classNameMapper);
+ if (builtClassMappings != null) {
+ builtClassMappings.addAll(pendingClassMappings);
+ }
+ pendingClassMappings.clear();
+ } catch (Exception e) {
+ throw new InvalidMappingFileException(e);
+ }
+ return classNameMapper;
+ }
+
+ private MapVersion getMapVersion() {
+ if (classNameMapper == null) {
+ return MapVersion.MAP_VERSION_NONE;
+ } else {
+ MapVersionMappingInformation mapVersion = classNameMapper.getFirstMappingInformation();
+ return mapVersion == null ? MapVersion.MAP_VERSION_UNKNOWN : mapVersion.getMapVersion();
+ }
+ }
+
+ @Override
+ public ProguardMappingSupplier registerClassUse(ClassReference classReference) {
+ if (!hasClassMappingFor(classReference.getTypeName())) {
+ pendingClassMappings.add(classReference.getTypeName());
+ }
+ return this;
+ }
+
+ @Override
+ public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
+ try (InputStream reader = proguardMapProducer.get()) {
+ VerifyMappingFileHashResult checkResult =
+ ProguardMapChecker.validateProguardMapHash(
+ CharStreams.toString(new InputStreamReader(reader, StandardCharsets.UTF_8)));
+ if (checkResult.isError()) {
+ diagnosticsHandler.error(new StringDiagnostic(checkResult.getMessage()));
+ throw new RuntimeException(checkResult.getMessage());
+ }
+ if (!checkResult.isOk()) {
+ diagnosticsHandler.warning(new StringDiagnostic(checkResult.getMessage()));
+ }
+ } catch (IOException e) {
+ diagnosticsHandler.error(new ExceptionDiagnostic(e));
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java
new file mode 100644
index 0000000..ab92a65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.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.retrace.internal;
+
+import com.android.tools.r8.retrace.ResultWithContext;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+
+public class ResultWithContextImpl<T> implements ResultWithContext<T> {
+
+ private final T result;
+ private final RetraceStackTraceContext context;
+
+ private ResultWithContextImpl(T result, RetraceStackTraceContext context) {
+ this.result = result;
+ this.context = context;
+ }
+
+ public static <T> ResultWithContext<T> create(T result, RetraceStackTraceContext context) {
+ return new ResultWithContextImpl<>(result, context);
+ }
+
+ @Override
+ public RetraceStackTraceContext getContext() {
+ return context;
+ }
+
+ @Override
+ public T getResult() {
+ return result;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index 346b417..daec745 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.retrace.MappingProvider;
+import com.android.tools.r8.retrace.MappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.Retracer;
@@ -21,14 +21,14 @@
/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
public class RetracerImpl implements Retracer {
- private final MappingProviderInternal classNameMapperProvider;
+ private final MappingSupplierInternal classNameMapperSupplier;
private final DiagnosticsHandler diagnosticsHandler;
private RetracerImpl(
- MappingProviderInternal classNameMapperProvider, DiagnosticsHandler diagnosticsHandler) {
- this.classNameMapperProvider = classNameMapperProvider;
+ MappingSupplierInternal classNameMapperSupplier, DiagnosticsHandler diagnosticsHandler) {
+ this.classNameMapperSupplier = classNameMapperSupplier;
this.diagnosticsHandler = diagnosticsHandler;
- assert classNameMapperProvider != null;
+ assert classNameMapperSupplier != null;
}
public DiagnosticsHandler getDiagnosticsHandler() {
@@ -70,7 +70,9 @@
@Override
public RetraceClassResultImpl retraceClass(ClassReference classReference) {
return RetraceClassResultImpl.create(
- classReference, classNameMapperProvider.getClassNaming(classReference.getTypeName()), this);
+ classReference,
+ classNameMapperSupplier.getClassNaming(diagnosticsHandler, classReference.getTypeName()),
+ this);
}
@Override
@@ -84,11 +86,12 @@
}
public Set<MapVersionMappingInformation> getMapVersions() {
- return classNameMapperProvider.getMapVersions();
+ return classNameMapperSupplier.getMapVersions(diagnosticsHandler);
}
public String getSourceFile(ClassReference classReference) {
- return classNameMapperProvider.getSourceFileForClass(classReference.getTypeName());
+ return classNameMapperSupplier.getSourceFileForClass(
+ diagnosticsHandler, classReference.getTypeName());
}
public static Builder builder() {
@@ -97,14 +100,14 @@
public static class Builder implements RetracerBuilder {
- private MappingProvider mappingProvider;
+ private MappingSupplier mappingSupplier;
private DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
private Builder() {}
@Override
- public Builder setMappingProvider(MappingProvider mappingProvider) {
- this.mappingProvider = mappingProvider;
+ public Builder setMappingSupplier(MappingSupplier mappingSupplier) {
+ this.mappingSupplier = mappingSupplier;
return this;
}
@@ -116,7 +119,7 @@
@Override
public RetracerImpl build() {
- return new RetracerImpl(mappingProvider, diagnosticsHandler);
+ return new RetracerImpl(mappingSupplier, diagnosticsHandler);
}
}
}
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 c731f7d..689a3f0 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -16,6 +16,8 @@
import static java.util.Collections.emptySet;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.androidapi.CovariantReturnTypeMethods;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -72,6 +74,7 @@
import com.android.tools.r8.graph.LookupMethodTarget;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
@@ -673,12 +676,23 @@
return definitionFor(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
+ public DexLibraryClass definitionForLibraryClassOrIgnore(DexType type) {
+ assert type.isClassType();
+ ClassResolutionResult classResolutionResult =
+ appInfo().contextIndependentDefinitionForWithResolutionResult(type);
+ return classResolutionResult.hasClassResolutionResult()
+ && !classResolutionResult.isMultipleClassResolutionResult()
+ ? DexLibraryClass.asLibraryClassOrNull(
+ classResolutionResult.toSingleClassWithProgramOverLibrary())
+ : null;
+ }
+
public boolean hasAlternativeLibraryDefinition(DexProgramClass programClass) {
ClassResolutionResult classResolutionResult =
internalDefinitionFor(
programClass.type, programClass, this::recordNonProgramClass, this::reportMissingClass);
assert classResolutionResult.hasClassResolutionResult();
- DexClass alternativeClass = classResolutionResult.toAlternativeClassWithProgramOverLibrary();
+ DexClass alternativeClass = classResolutionResult.toAlternativeClass();
assert alternativeClass == null || alternativeClass.isLibraryClass();
return alternativeClass != null;
}
@@ -772,6 +786,7 @@
// rules.
handleLibraryTypeInheritingFromProgramType(clazz.asLibraryClass());
}
+ analyses.forEach(analysis -> analysis.processNewLiveNonProgramType(clazz));
clazz.forEachClassField(
field ->
addNonProgramClassToWorklist(
@@ -2547,9 +2562,7 @@
return;
}
DexClass alternativeResolutionResult =
- appInfo()
- .contextIndependentDefinitionForWithResolutionResult(type)
- .toAlternativeClassWithProgramOverLibrary();
+ appInfo().contextIndependentDefinitionForWithResolutionResult(type).toAlternativeClass();
if (alternativeResolutionResult != null && alternativeResolutionResult.isLibraryClass()) {
// We are in a situation where a library class inherits from a library class, which has a
// program class duplicated version for low API levels.
@@ -3554,9 +3567,8 @@
includeMinimumKeepInfo(rootSet);
if (mode.isInitialTreeShaking()) {
- // This is simulating the effect of the "root set" applied rules.
- // This is done only in the initial pass, in subsequent passes the "rules" are reapplied
- // by iterating the instances.
+ // Amend library methods with covariant return types.
+ modelLibraryMethodsWithCovariantReturnTypes();
} else if (appView.getKeepInfo() != null) {
EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
appView
@@ -3598,6 +3610,30 @@
this::recordDependentMinimumKeepInfo);
}
+ private void modelLibraryMethodsWithCovariantReturnTypes() {
+ CovariantReturnTypeMethods.registerMethodsWithCovariantReturnType(
+ appView.dexItemFactory(),
+ method -> {
+ DexLibraryClass libraryClass =
+ DexLibraryClass.asLibraryClassOrNull(
+ appView.appInfo().definitionForWithoutExistenceAssert(method.getHolderType()));
+ if (libraryClass == null) {
+ return;
+ }
+ // Check if the covariant method exists on the class.
+ DexEncodedMethod covariantMethod = libraryClass.lookupMethod(method);
+ if (covariantMethod != null) {
+ return;
+ }
+ libraryClass.addVirtualMethod(
+ DexEncodedMethod.builder()
+ .setMethod(method)
+ .setAccessFlags(MethodAccessFlags.builder().setPublic().build())
+ .setApiLevelForDefinition(ComputedApiLevel.notSet())
+ .build());
+ });
+ }
+
private void applyMinimumKeepInfo(DexProgramClass clazz) {
EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
KeepClassInfo.Joiner minimumKeepInfoForClass =
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index 85a1b2a..1932bd0 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.DESCRIPTOR_VIVIFIED_PREFIX;
import static com.android.tools.r8.utils.collections.IdentityHashSetFromMap.newProgramDerivedContextSet;
+import com.android.tools.r8.androidapi.CovariantReturnTypeMethods;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
import com.android.tools.r8.diagnostic.internal.DefinitionContextUtils;
import com.android.tools.r8.diagnostic.internal.MissingClassInfoImpl;
@@ -292,6 +293,9 @@
addWithRewrittenType(
allowedMissingClasses, conversions.getTo().getHolderType(), appView);
});
+ CovariantReturnTypeMethods.registerMethodsWithCovariantReturnType(
+ dexItemFactory,
+ method -> method.getReferencedTypes().forEach(allowedMissingClasses::add));
return allowedMissingClasses.build();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 8b4210b..cf357ed 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS;
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
import com.android.tools.r8.InputDependencyGraphConsumer;
@@ -77,7 +78,7 @@
"allowruntypeandignoreoptimizationpasses",
"dontshrinkduringoptimization",
"convert_proto_enum_to_string",
- "keepkotlinmetadata");
+ "adaptkotlinmetadata");
private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS =
ImmutableList.of("isclassnamestring", "whyarenotsimple");
@@ -286,12 +287,23 @@
|| parseTestingOption(optionStart)
|| parseUnsupportedOptionAndErr(optionStart)) {
// Intentionally left empty.
- } else if (acceptString("adaptkotlinmetadata")) {
- reporter.info(
- new StringDiagnostic(
- "Ignoring -adaptkotlinmetadata because R8 always process kotlin.Metadata",
- origin,
- getPosition(optionStart)));
+ } else if (acceptString("keepkotlinmetadata")) {
+ configurationBuilder.addRule(
+ ProguardKeepRule.builder()
+ .setType(ProguardKeepRuleType.KEEP)
+ .setClassType(ProguardClassType.CLASS)
+ .setOrigin(origin)
+ .setStart(optionStart)
+ .setClassNames(
+ ProguardClassNameList.builder()
+ .addClassName(
+ false, ProguardTypeMatcher.create(dexItemFactory.kotlinMetadataType))
+ .build())
+ .setMemberRules(Collections.singletonList(ProguardMemberRule.defaultKeepAllRule()))
+ .setSource("-keepkotlinmetadata")
+ .build());
+ configurationBuilder.addKeepAttributePatterns(
+ Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS));
} else if (acceptString("renamesourcefileattribute")) {
skipWhitespace();
if (isOptionalArgumentGiven()) {
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 0cbbfed..97c4499 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -373,6 +373,7 @@
DexMethod method,
MethodResolutionResult resolutionResult,
Function<SingleResolutionResult<?>, DexClassAndMethod> getResult) {
+ BooleanBox seenSingleResult = new BooleanBox();
resolutionResult.forEachMethodResolutionResult(
result -> {
if (result.isFailedResolution()) {
@@ -382,13 +383,20 @@
type -> addType(type, referencedFrom),
methodCausingFailure ->
handleRewrittenMethodReference(method, methodCausingFailure));
- if (!result.asFailedResolution().hasTypesOrMethodsCausingError()) {
- handleRewrittenMethodReference(method, (DexEncodedMethod) null);
- }
return;
}
+ seenSingleResult.set();
handleRewrittenMethodReference(method, getResult.apply(result.asSingleResolution()));
});
+ if (seenSingleResult.isFalse()) {
+ resolutionResult.forEachMethodResolutionResult(
+ failingResult -> {
+ assert failingResult.isFailedResolution();
+ if (!failingResult.asFailedResolution().hasMethodsCausingError()) {
+ handleRewrittenMethodReference(method, (DexEncodedMethod) null);
+ }
+ });
+ }
}
private void handleRewrittenMethodReference(
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 374459a..189d9f9 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -4,12 +4,20 @@
package com.android.tools.r8.utils;
+import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
+
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LibraryClass;
+import com.android.tools.r8.graph.LibraryDefinition;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
@@ -95,11 +103,78 @@
if (apiLevelOfOriginal.isUnknownApiLevel()) {
return false;
}
- return apiLevelOfOriginal
- .asKnownApiLevel()
- .max(apiLevel)
- .asKnownApiLevel()
- .getApiLevel()
- .isLessThanOrEqualTo(options.getMinApiLevel());
+ return apiLevelOfOriginal.max(apiLevel).isLessThanOrEqualTo(options.getMinApiLevel()).isTrue();
+ }
+
+ private static boolean isApiSafeForReference(LibraryDefinition definition, AppView<?> appView) {
+ return isApiSafeForReference(definition, appView.apiLevelCompute(), appView.options());
+ }
+
+ private static boolean isApiSafeForReference(
+ LibraryDefinition definition,
+ AndroidApiLevelCompute androidApiLevelCompute,
+ InternalOptions options) {
+ assert options.apiModelingOptions().enableApiCallerIdentification;
+ ComputedApiLevel apiLevel =
+ androidApiLevelCompute.computeApiLevelForLibraryReference(
+ definition.getReference(), ComputedApiLevel.unknown());
+ return apiLevel.isLessThanOrEqualTo(options.getMinApiLevel()).isTrue();
+ }
+
+ private static boolean isApiSafeForReference(
+ LibraryDefinition newDefinition, LibraryDefinition oldDefinition, AppView<?> appView) {
+ assert appView.options().apiModelingOptions().enableApiCallerIdentification;
+ assert !isApiSafeForReference(newDefinition, appView)
+ : "Clients should first check if the definition is present on all apis since the min api";
+ AndroidApiLevelCompute androidApiLevelCompute = appView.apiLevelCompute();
+ ComputedApiLevel apiLevel =
+ androidApiLevelCompute.computeApiLevelForLibraryReference(
+ newDefinition.getReference(), ComputedApiLevel.unknown());
+ if (apiLevel.isUnknownApiLevel()) {
+ return false;
+ }
+ ComputedApiLevel apiLevelOfOriginal =
+ androidApiLevelCompute.computeApiLevelForLibraryReference(
+ oldDefinition.getReference(), ComputedApiLevel.unknown());
+ return apiLevel.isLessThanOrEqualTo(apiLevelOfOriginal).isTrue();
+ }
+
+ public static boolean isApiSafeForTypeStrengthening(
+ DexType newType, DexType oldType, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ // Type strengthening only applies to reference types.
+ assert newType.isReferenceType();
+ assert oldType.isReferenceType();
+ assert newType != oldType;
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexType newBaseType = newType.toBaseType(dexItemFactory);
+ if (newBaseType.isPrimitiveType()) {
+ // Array of primitives is available on all api levels.
+ return true;
+ }
+ assert newBaseType.isClassType();
+ DexClass newBaseClass = appView.definitionFor(newBaseType);
+ if (newBaseClass == null) {
+ // This could be a library class that is only available on newer api levels.
+ return false;
+ }
+ if (!newBaseClass.isLibraryClass()) {
+ // Program and classpath classes are not api level dependent.
+ return true;
+ }
+ if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ // Conservatively bail out if we don't have api modeling.
+ return false;
+ }
+ LibraryClass newBaseLibraryClass = newBaseClass.asLibraryClass();
+ if (isApiSafeForReference(newBaseLibraryClass, appView)) {
+ // Library class is present on all api levels since min api.
+ return true;
+ }
+ // Check if the new library class is present since the api level of the old type.
+ DexType oldBaseType = oldType.toBaseType(dexItemFactory);
+ assert oldBaseType.isClassType();
+ LibraryClass oldBaseLibraryClass = asLibraryClassOrNull(appView.definitionFor(oldBaseType));
+ return oldBaseLibraryClass != null
+ && isApiSafeForReference(newBaseLibraryClass, oldBaseLibraryClass, appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 292f103..17b3673 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8Command.Builder;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -22,6 +23,7 @@
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Consumer;
/**
* Wrapper to make it easy to call R8 in compat mode when compiling a dump file.
@@ -36,7 +38,13 @@
public class CompileDumpCompatR8 {
private static final List<String> VALID_OPTIONS =
- Arrays.asList("--classfile", "--compat", "--debug", "--release");
+ Arrays.asList(
+ "--classfile",
+ "--compat",
+ "--debug",
+ "--release",
+ "--enable-missing-library-api-modeling",
+ "--android-platform-build");
private static final List<String> VALID_OPTIONS_WITH_SINGLE_OPERAND =
Arrays.asList(
@@ -78,6 +86,8 @@
List<Path> mainDexRulesFiles = new ArrayList<>();
int minApi = 1;
int threads = -1;
+ boolean enableMissingLibraryApiModeling = false;
+ boolean androidPlatformBuild = false;
for (int i = 0; i < args.length; i++) {
String option = args[i];
if (VALID_OPTIONS.contains(option)) {
@@ -102,6 +112,12 @@
compilationMode = CompilationMode.RELEASE;
break;
}
+ case "--enable-missing-library-api-modeling":
+ enableMissingLibraryApiModeling = true;
+ break;
+ case "--android-platform-build":
+ androidPlatformBuild = true;
+ break;
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
@@ -187,6 +203,11 @@
.addMainDexRulesFiles(mainDexRulesFiles)
.setOutput(outputPath, outputMode)
.setMode(compilationMode);
+ getReflectiveBuilderMethod(
+ commandBuilder, "setEnableExperimentalMissingLibraryApiModeling", boolean.class)
+ .accept(new Object[] {enableMissingLibraryApiModeling});
+ getReflectiveBuilderMethod(commandBuilder, "setAndroidPlatformBuild", boolean.class)
+ .accept(new Object[] {androidPlatformBuild});
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
}
@@ -217,6 +238,24 @@
}
}
+ private static Consumer<Object[]> getReflectiveBuilderMethod(
+ Builder builder, String setter, Class<?>... parameters) {
+ try {
+ Method declaredMethod = CompatProguardCommandBuilder.class.getMethod(setter, parameters);
+ return args -> {
+ try {
+ declaredMethod.invoke(builder, args);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ };
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ // The option is not available so we just return an empty consumer
+ return args -> {};
+ }
+ }
+
// We cannot use StringResource since this class is added to the class path and has access only
// to the public APIs.
private static String readAllBytesJava7(Path filePath) {
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 c16b39f..37a55bb 100644
--- a/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
+++ b/src/main/java/com/android/tools/r8/utils/DepthFirstSearchWorkListBase.java
@@ -115,7 +115,7 @@
/** The joining of state during backtracking of the algorithm. */
abstract TraversalContinuation<TB, TC> internalOnJoin(T node);
- abstract List<TC> getFinalStateForRoots(Collection<N> roots);
+ protected abstract List<TC> getFinalStateForRoots(Collection<N> roots);
final T internalEnqueueNode(N value) {
T dfsNode = nodeToNodeWithStateMap.computeIfAbsent(value, this::createDfsNode);
@@ -190,8 +190,18 @@
@Override
protected TraversalContinuation<TB, TC> internalOnJoin(DFSNodeImpl<N> node) {
+ return joiner(node);
+ }
+
+ public TraversalContinuation<TB, TC> joiner(DFSNode<N> node) {
+ // Override to be notified during callback.
return TraversalContinuation.doContinue();
}
+
+ @Override
+ protected List<TC> getFinalStateForRoots(Collection<N> roots) {
+ return null;
+ }
}
public abstract static class StatefulDepthFirstSearchWorkList<N, S, TB>
@@ -254,7 +264,7 @@
}
@Override
- List<S> getFinalStateForRoots(Collection<N> roots) {
+ public 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/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index ee15be2..e74dfd9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
+import static com.android.tools.r8.utils.AndroidApiLevel.B;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
import com.android.tools.r8.ClassFileConsumer;
@@ -336,7 +337,6 @@
public boolean enableTreeShakingOfLibraryMethodOverrides = false;
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
- public boolean cfToCfDesugar = false;
public boolean forceAnnotateSynthetics = false;
public boolean readDebugSetFileEvent = false;
public boolean disableL8AnnotationRemoval =
@@ -518,12 +518,12 @@
}
public boolean shouldBackportMethods() {
- return !hasConsumer() || isGeneratingDex() || cfToCfDesugar;
+ return !hasConsumer() || isGeneratingDex() || isCfDesugaring();
}
public boolean shouldKeepStackMapTable() {
- assert cfToCfDesugar || isRelocatorCompilation() || getProguardConfiguration() != null;
- return cfToCfDesugar
+ assert isCfDesugaring() || isRelocatorCompilation() || getProguardConfiguration() != null;
+ return isCfDesugaring()
|| isRelocatorCompilation()
|| getProguardConfiguration().getKeepAttributes().stackMapTable;
}
@@ -549,7 +549,11 @@
}
public boolean isDesugaring() {
- return !isGeneratingClassFiles() || cfToCfDesugar;
+ return desugarState.isOn();
+ }
+
+ public boolean isCfDesugaring() {
+ return isGeneratingClassFiles() && desugarState.isOn();
}
public DexIndexedConsumer getDexIndexedConsumer() {
@@ -646,6 +650,10 @@
private final boolean enableMinification;
public AndroidApiLevel getMinApiLevel() {
+ // If compiling to CF with no desugaring then we should not inspect the min-api.
+ // For now we assert the API level for non-desugared CF is B, but it would be better to never
+ // access the min-api in those cases.
+ assert desugarState.isOn() || isGeneratingDex() || minApiLevel.equals(AndroidApiLevel.B);
return minApiLevel;
}
@@ -800,6 +808,7 @@
private final CallSiteOptimizationOptions callSiteOptimizationOptions =
new CallSiteOptimizationOptions();
+ private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions();
private final ClassInlinerOptions classInlinerOptions = new ClassInlinerOptions();
private final InlinerOptions inlinerOptions = new InlinerOptions();
private final HorizontalClassMergerOptions horizontalClassMergerOptions =
@@ -859,6 +868,10 @@
return desugarSpecificOptions;
}
+ public CfCodeAnalysisOptions getCfCodeAnalysisOptions() {
+ return cfCodeAnalysisOptions;
+ }
+
public OpenClosedInterfacesOptions getOpenClosedInterfacesOptions() {
return openClosedInterfacesOptions;
}
@@ -937,7 +950,7 @@
// If non null it must be and passed to the consumer.
public StringConsumer mainDexListConsumer = null;
- // If null, no proguad map needs to be computed.
+ // If null, no proguard map needs to be computed.
// If non null it must be and passed to the consumer.
public StringConsumer proguardMapConsumer = null;
@@ -1391,6 +1404,31 @@
}
}
+ public static class CfCodeAnalysisOptions {
+
+ private boolean allowUnreachableCfBlocks = true;
+ private boolean enableUnverifiableCodeReporting = false;
+
+ public boolean isUnverifiableCodeReportingEnabled() {
+ return enableUnverifiableCodeReporting;
+ }
+
+ public boolean isUnreachableCfBlocksAllowed() {
+ return allowUnreachableCfBlocks;
+ }
+
+ public CfCodeAnalysisOptions setAllowUnreachableCfBlocks(boolean allowUnreachableCfBlocks) {
+ this.allowUnreachableCfBlocks = allowUnreachableCfBlocks;
+ return this;
+ }
+
+ public CfCodeAnalysisOptions setEnableUnverifiableCodeReporting(
+ boolean enableUnverifiableCodeReporting) {
+ this.enableUnverifiableCodeReporting = enableUnverifiableCodeReporting;
+ return this;
+ }
+ }
+
public class ClassInlinerOptions {
public int classInliningInstructionAllowance = -1;
@@ -1973,6 +2011,36 @@
}
/**
+ * Predicate to guard on the support of a language feature.
+ *
+ * <p>Note that if not desugaring or compiling to DEX, then the output is a mapping of the input
+ * and thus all parts should be representable (assuming the compiler has support for them).
+ */
+ private boolean hasFeaturePresentFrom(AndroidApiLevel level) {
+ if (desugarState.isOn() || isGeneratingDex()) {
+ return level != null && hasMinApi(level);
+ }
+ // If not desugaring and not compiling to DEX, then the API level is effectively ignored and
+ // we assume that everything in the input is supported in the output.
+ assert minApiLevel.equals(B);
+ return true;
+ }
+
+ /**
+ * Predicate to guard against the possible presence of a VM bug.
+ *
+ * <p>Note that if the compilation is not desugaring to a min-api or targeting DEX at a min-api,
+ * then the bug is assumed to be present as the CF output could be futher compiled to any target.
+ */
+ private boolean canHaveBugPresentUntil(AndroidApiLevel level) {
+ if (desugarState.isOn() || isGeneratingDex()) {
+ return level == null || !hasMinApi(level);
+ }
+ assert minApiLevel.equals(B);
+ return true;
+ }
+
+ /**
* Allow access modification of synthetic lambda implementation methods in D8 to avoid generating
* an excessive amount of accessibility bridges. In R8, the lambda implementation methods are
* inlined into the synthesized accessibility bridges, thus we don't allow access modification.
@@ -2008,47 +2076,52 @@
}
public boolean canUseInvokePolymorphicOnVarHandle() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.P);
+ return hasFeaturePresentFrom(AndroidApiLevel.P);
}
public boolean canUseInvokePolymorphic() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.O);
+ return hasFeaturePresentFrom(AndroidApiLevel.O);
}
public boolean canUseConstantMethodHandle() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.P);
+ return hasFeaturePresentFrom(AndroidApiLevel.P);
}
public boolean canUseConstantMethodType() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.P);
+ return hasFeaturePresentFrom(AndroidApiLevel.P);
}
public boolean canUseInvokeCustom() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.O);
+ return hasFeaturePresentFrom(AndroidApiLevel.O);
}
public boolean canUseDefaultAndStaticInterfaceMethods() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.N);
+ return hasFeaturePresentFrom(AndroidApiLevel.N);
}
public boolean canUseNestBasedAccess() {
- return !isDesugaring() || emitNestAnnotationsInDex;
+ return hasFeaturePresentFrom(null) || emitNestAnnotationsInDex;
}
public boolean canUseRecords() {
- return !isDesugaring();
+ return hasFeaturePresentFrom(null);
}
public boolean canUseSealedClasses() {
- return !isDesugaring() || emitPermittedSubclassesAnnotationsInDex;
+ return hasFeaturePresentFrom(null) || emitPermittedSubclassesAnnotationsInDex;
}
public boolean canLeaveStaticInterfaceMethodInvokes() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.L);
+ return hasFeaturePresentFrom(AndroidApiLevel.L);
}
public boolean canUseTwrCloseResourceMethod() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.K);
+ }
+
+ public boolean canUseSpacesInSimpleName() {
+ return itemFactory.getSkipNameValidationForTesting()
+ || hasFeaturePresentFrom(AndroidApiLevel.R);
}
public boolean enableBackportedMethodRewriting() {
@@ -2070,7 +2143,7 @@
}
public boolean canUsePrivateInterfaceMethods() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.N);
+ return hasFeaturePresentFrom(AndroidApiLevel.N);
}
// Debug entries may be dropped only if the source file content allows being omitted from
@@ -2095,7 +2168,7 @@
if (!hasConsumer()) {
return false;
}
- return desugarState == DesugarState.ON
+ return desugarState.isOn()
&& interfaceMethodDesugaring == OffOrAuto.Auto
&& !canUseDefaultAndStaticInterfaceMethods();
}
@@ -2114,23 +2187,24 @@
}
public boolean canUseJavaUtilObjects() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public boolean canUseJavaUtilObjectsIsNull() {
- return isGeneratingDex() && hasMinApi(AndroidApiLevel.N);
+ return hasFeaturePresentFrom(AndroidApiLevel.N);
}
public boolean canUseSuppressedExceptions() {
// TODO(b/214239152): Suppressed exceptions are @hide from at least 4.0.1 / Android I / API 14.
- return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public boolean canUseAssertionErrorTwoArgumentConstructor() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.K);
}
public CfVersion classFileVersionAfterDesugaring(CfVersion version) {
+ assert isGeneratingClassFiles();
if (!isDesugaring()) {
return version;
}
@@ -2146,7 +2220,7 @@
//
// https://android.googlesource.com/platform/libcore/+/refs/heads/ics-mr1/luni/src/main/java/java/lang/AssertionError.java#56
public boolean canInitCauseAfterAssertionErrorObjectConstructor() {
- return !isDesugaring() || hasMinApi(AndroidApiLevel.J);
+ return hasFeaturePresentFrom(AndroidApiLevel.J);
}
// Dalvik x86-atom backend had a bug that made it crash on filled-new-array instructions for
@@ -2159,7 +2233,7 @@
// https://android.googlesource.com/platform/dalvik/+/ics-mr0/vm/mterp/out/InterpAsm-x86-atom.S#25106
public boolean canUseFilledNewArrayOfObjects() {
assert isGeneratingDex();
- return hasMinApi(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.K);
}
// Art had a bug (b/68761724) for Android N and O in the arm32 interpreter
@@ -2167,7 +2241,7 @@
// and the first register of the result could lead to the wrong exception
// being thrown on out of bounds.
public boolean canUseSameArrayAndResultRegisterInArrayGetWide() {
- return isGeneratingClassFiles() || getMinApiLevel().isGreaterThan(AndroidApiLevel.O_MR1);
+ return hasFeaturePresentFrom(AndroidApiLevel.P);
}
// Some Lollipop versions of Art found in the wild perform invalid bounds
@@ -2184,7 +2258,7 @@
//
// See b/69364976 and b/77996377.
public boolean canHaveBoundsCheckEliminationBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// MediaTek JIT compilers for KitKat phones did not implement the not
@@ -2193,14 +2267,14 @@
// we can only use not instructions if we are targeting Art-based
// phones.
public boolean canUseNotInstruction() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.L);
+ return hasFeaturePresentFrom(AndroidApiLevel.L);
}
// Art before M has a verifier bug where the type of the contents of the receiver register is
// assumed to not change. If the receiver register is reused for something else the verifier
// will fail and the code will not run.
public boolean canHaveThisTypeVerifierBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Art crashes if we do dead reference elimination of the receiver in release mode and Art
@@ -2209,13 +2283,13 @@
//
// See b/116683601 and b/116837585.
public boolean canHaveThisJitCodeDebuggingBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.Q);
+ return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
// the first part of the result long before reading the second part of the input longs.
public boolean canHaveOverlappingLongRegisterBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
@@ -2248,7 +2322,7 @@
//
// See b/75408029.
public boolean canHaveCmpLongBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some Lollipop VMs crash if there is a const instruction between a cmp and an if instruction.
@@ -2276,7 +2350,7 @@
//
// See b/115552239.
public boolean canHaveCmpIfFloatBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
@@ -2298,7 +2372,7 @@
//
// This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
public boolean canHaveMul2AddrBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Marshmallow VMs create an incorrect doubly-linked list of instructions. When the VM
@@ -2307,7 +2381,7 @@
//
// See b/77842465.
public boolean canHaveDex2OatLinkedListBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.N);
+ return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// dex2oat on Marshmallow VMs does aggressive inlining which can eat up all the memory on
@@ -2315,7 +2389,7 @@
//
// See b/111960171
public boolean canHaveDex2OatInliningIssue() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.N);
+ return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// Art 7.0.0 and later Art JIT may perform an invalid optimization if a string new-instance does
@@ -2323,7 +2397,7 @@
//
// See b/78493232 and b/80118070.
public boolean canHaveArtStringNewInitBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.Q);
+ return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Dalvik tracing JIT may perform invalid optimizations when int/float values are converted to
@@ -2331,7 +2405,7 @@
//
// See b/77496850.
public boolean canHaveNumberConversionRegisterAllocationBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Some Lollipop mediatek VMs have a peculiar bug where the inliner crashes if there is a
@@ -2344,7 +2418,7 @@
//
// See b/68378480.
public boolean canHaveForwardingInitInliningBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// Some Lollipop x86_64 VMs have a bug causing a segfault if an exception handler directly targets
@@ -2356,7 +2430,8 @@
//
// See b/111337896.
public boolean canHaveExceptionTargetingLoopHeaderBug() {
- return isGeneratingDex() && !debug && getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ assert isGeneratingDex();
+ return !debug && canHaveBugPresentUntil(AndroidApiLevel.M);
}
// The Dalvik tracing JIT can trace past the end of the instruction stream and end up
@@ -2371,7 +2446,7 @@
// We also could not insert any dead code (e.g. a return) because that would make mediatek
// dominator calculations on 7.0.0 crash. See b/128926846.
public boolean canHaveTracingPastInstructionsStreamBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// The art verifier incorrectly propagates type information for the following pattern:
@@ -2398,7 +2473,7 @@
//
// Fixed in Android Q, see b/120985556.
public boolean canHaveArtInstanceOfVerifierBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.Q);
+ return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Some Art Lollipop version do not deal correctly with long-to-int conversions.
@@ -2421,7 +2496,7 @@
public boolean canHaveLongToIntBug() {
// We have only seen this happening on Lollipop arm64 backends. We have tested on
// Marshmallow and Nougat arm64 devices and they do not have the bug.
- return getMinApiLevel().isLessThan(AndroidApiLevel.M);
+ return canHaveBugPresentUntil(AndroidApiLevel.M);
}
// The Art VM for Android N through P has a bug in the JIT that means that if the same
@@ -2434,7 +2509,7 @@
//
// See b/120164595.
public boolean canHaveExceptionTypeBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.Q);
+ return canHaveBugPresentUntil(AndroidApiLevel.Q);
}
// Art 4.0.4 fails with a verification error when a null-literal is being passed directly to an
@@ -2442,7 +2517,7 @@
// elimination of check-cast instructions where the value being cast is the constant null.
// See b/123269162.
public boolean canHaveArtCheckCastVerifierBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.J);
+ return canHaveBugPresentUntil(AndroidApiLevel.J);
}
// The verifier will merge A[] and B[] to Object[], even when both A and B implement an interface
@@ -2466,7 +2541,7 @@
//
// See b/131349148
public boolean canHaveDalvikCatchHandlerVerificationBug() {
- return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// Having an invoke instruction that targets an abstract method on a non-abstract class will fail
@@ -2474,7 +2549,7 @@
//
// See b/132953944.
public boolean canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// On dalvik we see issues when using an int value in places where a boolean, byte, char, or short
@@ -2488,14 +2563,14 @@
//
// See also b/134304597 and b/124152497.
public boolean canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug() {
- return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.L);
+ return canHaveBugPresentUntil(AndroidApiLevel.L);
}
// The standard library prior to API 19 did not contain a ZipFile that implemented Closable.
//
// See b/177532008.
public boolean canHaveZipFileWithMissingCloseableBug() {
- return isGeneratingClassFiles() || getMinApiLevel().isLessThan(AndroidApiLevel.K);
+ return canHaveBugPresentUntil(AndroidApiLevel.K);
}
// Some versions of Dalvik had a bug where a switch with a MAX_INT key would still go to
@@ -2503,7 +2578,7 @@
//
// See b/177790310.
public boolean canHaveSwitchMaxIntBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.K);
+ return canHaveBugPresentUntil(AndroidApiLevel.K);
}
// On Dalvik the methods Integer.parseInt and Long.parseLong does not support strings with a '+'
@@ -2511,7 +2586,7 @@
//
// See b/182137865.
public boolean canParseNumbersWithPlusPrefix() {
- return getMinApiLevel().isGreaterThan(AndroidApiLevel.K);
+ return hasFeaturePresentFrom(AndroidApiLevel.L);
}
// Lollipop and Marshmallow devices do not correctly handle invoke-super when the static holder
@@ -2519,7 +2594,7 @@
//
// See b/215573892.
public boolean canHaveSuperInvokeBug() {
- return getMinApiLevel().isLessThan(AndroidApiLevel.N);
+ return canHaveBugPresentUntil(AndroidApiLevel.N);
}
// Some Dalvik and Art MVs does not support interface invokes to Object
@@ -2535,12 +2610,12 @@
//
// See b/218298666.
public boolean canHaveInvokeInterfaceToObjectMethodBug() {
- return isGeneratingDex() && getMinApiLevel().isLessThan(AndroidApiLevel.P);
+ return canHaveBugPresentUntil(AndroidApiLevel.P);
}
// Until we fully drop support for API levels < 16, we have to emit an empty annotation set to
// work around a DALVIK bug. See b/36951668.
public boolean canHaveDalvikEmptyAnnotationSetBug() {
- return minApiLevel.isLessThan(AndroidApiLevel.J_MR1);
+ return canHaveBugPresentUntil(AndroidApiLevel.J_MR1);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index bf80d7a..94b9bfe 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -9,6 +9,7 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
@@ -100,4 +101,19 @@
});
return result;
}
+
+ public static <K, V> boolean equals(Map<K, V> one, Map<K, V> other) {
+ if (one == other) {
+ return true;
+ }
+ if (one.size() != other.size()) {
+ return false;
+ }
+ for (Entry<K, V> firstEntry : one.entrySet()) {
+ if (!firstEntry.getValue().equals(other.get(firstEntry.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalUtils.java b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
index 25e1be0..627ee79 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -18,6 +20,16 @@
return traversal.apply(TraversalContinuation::doBreak).asBreak().getValue();
}
+ public static <BT, CT> boolean hasNext(
+ Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal) {
+ return !isEmpty(traversal);
+ }
+
+ public static <BT, CT> boolean isEmpty(
+ Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal) {
+ return isSizeExactly(traversal, 0);
+ }
+
public static <BT, CT> boolean isSingleton(
Consumer<Function<CT, TraversalContinuation<BT, CT>>> traversal) {
return isSizeExactly(traversal, 1);
@@ -54,4 +66,23 @@
}
return traversalContinuation;
}
+
+ public static <S, T, BT, CT> TraversalContinuation<BT, CT> traverseMap(
+ Map<S, T> map,
+ TriFunction<? super S, ? super T, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ TraversalContinuation<BT, CT> traversalContinuation =
+ TraversalContinuation.doContinue(initialValue);
+ for (Entry<S, T> entry : map.entrySet()) {
+ traversalContinuation =
+ fn.apply(
+ entry.getKey(),
+ entry.getValue(),
+ traversalContinuation.asContinue().getValueOrDefault(null));
+ if (traversalContinuation.isBreak()) {
+ break;
+ }
+ }
+ return traversalContinuation;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java b/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java
new file mode 100644
index 0000000..e3aac9b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java
@@ -0,0 +1,48 @@
+// 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 com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.MethodReference;
+
+@Keep
+public class UnverifiableCfCodeDiagnostic implements Diagnostic {
+
+ private final MethodReference methodReference;
+ private final int instructionIndex;
+ private final String message;
+ private final Origin origin;
+
+ public UnverifiableCfCodeDiagnostic(
+ MethodReference methodReference, int instructionIndex, String message, Origin origin) {
+ this.methodReference = methodReference;
+ this.instructionIndex = instructionIndex;
+ this.message = message;
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Unverifiable code in `"
+ + MethodReferenceUtils.toSourceString(methodReference)
+ + "` at instruction "
+ + instructionIndex
+ + ": "
+ + message
+ + ".";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
index 8292f14..ebba98d 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
@@ -77,5 +77,9 @@
.removeIf(entry -> predicate.test(entry.getKey().get(), entry.getValue()));
}
+ public int size() {
+ return backing.size();
+ }
+
abstract Wrapper<K> wrap(K member);
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index c592df2..aebb5a6 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.R8CommandTest.getOutputPath;
import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -699,6 +701,36 @@
}
@Test
+ public void desugaredLibraryWithOutputConf() throws CompilationFailedException, IOException {
+ Path pgout = temp.getRoot().toPath().resolve("pgout.conf");
+ D8Command d8Command =
+ parse(
+ "--desugared-lib",
+ "src/library_desugar/desugar_jdk_libs.json",
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.R).toString(),
+ "--desugared-lib-pg-conf-output",
+ pgout.toString());
+ InternalOptions options = getOptionsWithLoadedDesugaredLibraryConfiguration(d8Command, false);
+ assertFalse(options.machineDesugaredLibrarySpecification.getRewriteType().isEmpty());
+ }
+
+ @Test
+ public void desugaredLibraryWithOutputConfMissingArg() {
+ TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+ try {
+ parse(
+ diagnostics,
+ "--desugared-lib",
+ "src/library_desugar/desugar_jdk_libs.json",
+ "--desugared-lib-pg-conf-output");
+ fail("Expected parse error");
+ } catch (CompilationFailedException e) {
+ diagnostics.assertErrorsMatch(diagnosticMessage(containsString("Missing parameter")));
+ }
+ }
+
+ @Test
public void pgInputMap() throws CompilationFailedException, IOException, ResourceException {
Path mapFile = temp.newFile().toPath();
FileUtils.writeTextFile(
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index c9f0ee8..3186dc1 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -89,6 +89,11 @@
return this;
}
+ public L8TestBuilder addKeepRules(String keepRule) throws IOException {
+ this.keepRules.add(keepRule);
+ return this;
+ }
+
public L8TestBuilder addKeepRuleFile(Path keepRuleFile) throws IOException {
this.keepRules.add(FileUtils.readTextFile(keepRuleFile, StandardCharsets.UTF_8));
return this;
diff --git a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
index 1d45cb1..79b8799 100644
--- a/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/R8CFRunExamplesJava9Test.java
@@ -58,11 +58,6 @@
@Override
void run() throws Throwable {
- boolean expectedToThrow = minSdkErrorExpectedCf(testName);
- if (expectedToThrow) {
- thrown.expect(ApiLevelException.class);
- }
-
String qualifiedMainClass = packageName + "." + mainClass;
Path inputFile = getInputJar();
Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
@@ -82,10 +77,6 @@
}
execute(testName, qualifiedMainClass, new Path[] {inputFile}, new Path[] {out}, args);
-
- if (expectedToThrow) {
- System.out.println("Did not throw ApiLevelException as expected");
- }
}
@Override
@@ -138,13 +129,4 @@
System.out.println(testName + " " + expectedFailures.contains(testName));
return expectedFailures.contains(testName);
}
-
- private static List<String> minSdkErrorExpected =
- ImmutableList.of(
- );
-
- private boolean minSdkErrorExpectedCf(String testName) {
- System.out.println(testName);
- return minSdkErrorExpected.contains(testName);
- }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index d7a6cc2..8b4c482 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.testing.AndroidBuildVersion;
@@ -59,6 +58,10 @@
options.testing.reportUnusedProguardConfigurationRules = true;
options.horizontalClassMergerOptions().enable();
options.horizontalClassMergerOptions().setEnableInterfaceMerging();
+ options
+ .getCfCodeAnalysisOptions()
+ .setAllowUnreachableCfBlocks(false)
+ .setEnableUnverifiableCodeReporting(true);
options.getOpenClosedInterfacesOptions().disallowOpenInterfaces();
};
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 847f4ae..40a27ee 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -102,10 +102,18 @@
return apiLevel;
}
+ public Path getDefaultAndroidJar() {
+ assert isDexRuntime();
+ return ToolHelper.getFirstSupportedAndroidJar(getApiLevel());
+ }
+
+ public Path getDefaultAndroidJarAbove(AndroidApiLevel minimumCompileApiLevel) {
+ assert isDexRuntime();
+ return ToolHelper.getFirstSupportedAndroidJar(getApiLevel().max(minimumCompileApiLevel));
+ }
+
public Path getDefaultRuntimeLibrary() {
- return isCfRuntime()
- ? ToolHelper.getJava8RuntimeJar()
- : ToolHelper.getFirstSupportedAndroidJar(getApiLevel());
+ return isCfRuntime() ? ToolHelper.getJava8RuntimeJar() : getDefaultAndroidJar();
}
// Access to underlying runtime/wrapper.
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 58e5901..bf47b6e 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -176,7 +176,7 @@
}
public T addKeepKotlinMetadata() {
- return addKeepRules("-keep class kotlin.Metadata { *; }");
+ return addKeepRules("-keepkotlinmetadata");
}
public T addKeepAllClassesRule() {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index b22d6d3..4330b97 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -15,7 +15,9 @@
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.desugar.desugaredlibrary.jdk11.ConversionConverter;
import com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
@@ -187,10 +189,13 @@
public static final Path DESUGARED_JDK_11_LIB_JAR =
Paths.get(OPEN_JDK_DIR + "desugar_jdk_libs_11/desugar_jdk_libs.jar");
+ public static Path getConvertedDesugaredLibConversions(CustomConversionVersion legacy) {
+ return ConversionConverter.convertJar(DESUGAR_LIB_CONVERSIONS, legacy);
+ }
+
public static Path getUndesugaredJdk11LibJarForTesting() {
return DesugaredLibraryJDK11Undesugarer.undesugaredJarJDK11(
- Paths.get("build/libs"),
- Paths.get(OPEN_JDK_DIR + "desugar_jdk_libs_11/desugar_jdk_libs.jar"));
+ Paths.get("build/libs"), DESUGARED_JDK_11_LIB_JAR);
}
public static boolean isLocalDevelopment() {
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
index db640a2..cf867dd 100644
--- a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
@@ -21,13 +21,16 @@
import com.android.tools.r8.apimodel.JavaSourceCodePrinter.ParameterizedType;
import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ClassReferenceUtils;
import com.android.tools.r8.utils.EntryUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -40,10 +43,12 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -81,7 +86,9 @@
public void testCanFindAnnotatedMethodsInJar() throws Exception {
CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
// These assertions are here to ensure we produce a sane result.
- assertEquals(51, covariantMethodsInJar.methodReferenceMap.size());
+ assertEquals(9, covariantMethodsInJar.methodReferenceMap.keySet().size());
+ assertEquals(
+ 51, covariantMethodsInJar.methodReferenceMap.values().stream().mapToLong(List::size).sum());
}
@Test
@@ -91,11 +98,9 @@
public static String generateCode() throws Exception {
CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
- Map<MethodReference, List<MethodReference>> methodReferenceMap =
- covariantMethodsInJar.methodReferenceMap;
- List<Entry<MethodReference, List<MethodReference>>> entries =
- new ArrayList<>(methodReferenceMap.entrySet());
- entries.sort(Entry.comparingByKey(MethodReferenceUtils.getMethodReferenceComparator()));
+ List<Entry<ClassReference, List<MethodReferenceWithApiLevel>>> entries =
+ new ArrayList<>(covariantMethodsInJar.methodReferenceMap.entrySet());
+ entries.sort(Entry.comparingByKey(ClassReferenceUtils.getClassReferenceComparator()));
JavaSourceCodePrinter printer =
JavaSourceCodePrinter.builder()
.setHeader(
@@ -119,10 +124,16 @@
methodPrinter ->
entries.forEach(
EntryUtils.accept(
- (ignored, covariations) ->
- covariations.forEach(
- covariant ->
- registerCovariantMethod(methodPrinter, covariant)))))
+ (ignored, covariations) -> {
+ covariations.sort(
+ Comparator.comparing(
+ MethodReferenceWithApiLevel::getMethodReference,
+ MethodReferenceUtils.getMethodReferenceComparator()));
+ covariations.forEach(
+ covariant ->
+ registerCovariantMethod(
+ methodPrinter, covariant.methodReference));
+ })))
.toString();
Path tempFile = Files.createTempFile("output-", ".java");
Files.write(tempFile, javaSourceCode.getBytes(StandardCharsets.UTF_8));
@@ -175,15 +186,15 @@
}
public static class CovariantMethodsInJarResult {
- private final Map<MethodReference, List<MethodReference>> methodReferenceMap;
+ private final Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap;
private CovariantMethodsInJarResult(
- Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap) {
this.methodReferenceMap = methodReferenceMap;
}
public static CovariantMethodsInJarResult create() throws Exception {
- Map<MethodReference, List<MethodReference>> methodReferenceMap = new HashMap<>();
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap = new HashMap<>();
CodeInspector inspector = new CodeInspector(PATH_TO_CORE_JAR);
DexItemFactory factory = inspector.getFactory();
for (FoundClassSubject clazz : inspector.allClasses()) {
@@ -196,6 +207,7 @@
isCovariantReturnTypeAnnotation(annotation.annotation, factory));
if (!covariantAnnotations.isEmpty()) {
MethodReference methodReference = method.asMethodReference();
+ ClassReference holder = clazz.getOriginalReference();
for (DexAnnotation covariantAnnotation : covariantAnnotations) {
if (covariantAnnotation.annotation.type
== factory.annotationCovariantReturnType) {
@@ -214,17 +226,55 @@
private static void createCovariantMethodReference(
MethodReference methodReference,
DexAnnotation covariantAnnotation,
- Map<MethodReference, List<MethodReference>> methodReferenceMap) {
+ Map<ClassReference, List<MethodReferenceWithApiLevel>> methodReferenceMap) {
DexValueType newReturnType =
covariantAnnotation.annotation.getElement(0).getValue().asDexValueType();
+ DexAnnotationElement element = covariantAnnotation.annotation.getElement(1);
+ assert element.name.toString().equals("presentAfter");
+ AndroidApiLevel apiLevel =
+ AndroidApiLevel.getAndroidApiLevel(element.getValue().asDexValueInt().value);
methodReferenceMap
- .computeIfAbsent(methodReference, ignoreKey(ArrayList::new))
+ .computeIfAbsent(methodReference.getHolderClass(), ignoreKey(ArrayList::new))
.add(
- Reference.method(
- methodReference.getHolderClass(),
- methodReference.getMethodName(),
- methodReference.getFormalTypes(),
- newReturnType.value.asClassReference()));
+ new MethodReferenceWithApiLevel(
+ Reference.method(
+ methodReference.getHolderClass(),
+ methodReference.getMethodName(),
+ methodReference.getFormalTypes(),
+ newReturnType.value.asClassReference()),
+ apiLevel));
+ }
+
+ public void visitCovariantMethodsForHolder(
+ ClassReference reference, Consumer<MethodReferenceWithApiLevel> consumer) {
+ List<MethodReferenceWithApiLevel> methodReferences = methodReferenceMap.get(reference);
+ if (methodReferences != null) {
+ methodReferences.stream()
+ .sorted(
+ Comparator.comparing(
+ MethodReferenceWithApiLevel::getMethodReference,
+ MethodReferenceUtils.getMethodReferenceComparator()))
+ .forEach(consumer);
+ }
+ }
+ }
+
+ public static class MethodReferenceWithApiLevel {
+
+ private final MethodReference methodReference;
+ private final AndroidApiLevel apiLevel;
+
+ private MethodReferenceWithApiLevel(MethodReference methodReference, AndroidApiLevel apiLevel) {
+ this.methodReference = methodReference;
+ this.apiLevel = apiLevel;
+ }
+
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ public AndroidApiLevel getApiLevel() {
+ return apiLevel;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index 708858e..423850e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.androidapi.AndroidApiDataAccess;
+import com.android.tools.r8.androidapi.GenerateCovariantReturnTypeMethodsTest.CovariantMethodsInJarResult;
import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -81,6 +82,8 @@
computeAppViewWithClassHierarchy(AndroidApp.builder().addLibraryFile(androidJar).build());
DexItemFactory factory = appView.dexItemFactory();
+ CovariantMethodsInJarResult covariantMethodsInJar = CovariantMethodsInJarResult.create();
+
for (ParsedApiClass apiClass : apiClasses) {
Map<DexMethod, AndroidApiLevel> methodsForApiClass = new HashMap<>();
apiClass.visitMethodReferences(
@@ -88,6 +91,18 @@
methods.forEach(
method -> methodsForApiClass.put(factory.createMethod(method), apiLevel));
});
+ covariantMethodsInJar.visitCovariantMethodsForHolder(
+ apiClass.getClassReference(),
+ methodReferenceWithApiLevel -> {
+ DexMethod method =
+ factory.createMethod(methodReferenceWithApiLevel.getMethodReference());
+ if (!methodsForApiClass.containsKey(method)) {
+ apiClass.amendCovariantMethod(
+ methodReferenceWithApiLevel.getMethodReference(),
+ methodReferenceWithApiLevel.getApiLevel());
+ methodsForApiClass.put(method, methodReferenceWithApiLevel.getApiLevel());
+ }
+ });
Map<DexField, AndroidApiLevel> fieldsForApiClass = new HashMap<>();
apiClass.visitFieldReferences(
(apiLevel, fields) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 53f5fda..7de78a2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -6,6 +6,7 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -110,7 +111,7 @@
@Test
public void testDatabaseGenerationUpToDate() throws Exception {
GenerateDatabaseResourceFilesResult result = generateResourcesFiles();
- TestBase.filesAreEqual(result.apiLevels, API_DATABASE);
+ assertTrue(TestBase.filesAreEqual(result.apiLevels, API_DATABASE));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 4a3cfa2..8e8f4ee 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -247,6 +247,10 @@
interfaces.forEach(consumer);
}
+ public void amendCovariantMethod(MethodReference methodReference, AndroidApiLevel apiLevel) {
+ register(methodReference, apiLevel);
+ }
+
public boolean isInterface() {
return isInterface;
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelBridgeToLibraryMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelBridgeToLibraryMethodTest.java
new file mode 100644
index 0000000..374b190
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelBridgeToLibraryMethodTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.function.Function;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for b/235184674.
+@RunWith(Parameterized.class)
+public class ApiModelBridgeToLibraryMethodTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("8");
+
+ @Test()
+ public void testR8WithApiLevelCheck() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClassWithApiLevelCheck.class)
+ .addAndroidBuildVersion()
+ .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+ .applyIf(
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
+ r -> r.assertSuccessWithOutputLines("No call"),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ static class TestClassWithApiLevelCheck {
+
+ private static void m(B b) {
+ System.out.println(b.compose(b).apply(2));
+ }
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 24) {
+ m(new B());
+ } else {
+ System.out.println("No call");
+ }
+ }
+ }
+
+ interface MyFunction<V, R> extends Function<V, R> {}
+
+ static class B implements MyFunction<Integer, Integer> {
+
+ @Override
+ public <V> Function<V, Integer> compose(Function<? super V, ? extends Integer> before) {
+ return MyFunction.super.compose(before);
+ }
+
+ @Override
+ public Integer apply(Integer integer) {
+ return integer * 2;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
index 0726238..524147f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -40,17 +40,17 @@
testForR8(parameters.getBackend())
.addProgramClasses(Main.class)
.setMinApi(parameters.getApiLevel())
- .applyIf(
- parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- b -> b.addDontWarn(KeySetView.class))
.addKeepMainRule(Main.class)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/232891189): Should be api level 28.
- assertNull(apiLevel);
+ assertEquals(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.P
+ : parameters.getApiLevel().max(AndroidApiLevel.P),
+ apiLevel);
}
}))
.compile();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java
new file mode 100644
index 0000000..d974c7a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+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 ApiModelTypeStrengtheningAboveMinApiTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ int sdkInt = parameters.isCfRuntime() ? 0 : parameters.getApiLevel().getLevel();
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, Version.class)
+ .addLibraryClasses(ApiLevel22.class, ApiLevel23.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-assumevalues class " + Version.class.getTypeName() + " {",
+ " public static int getSdkInt(int) return " + sdkInt + "..42;",
+ "}")
+ .apply(setMockApiLevelForClass(ApiLevel22.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForClass(ApiLevel23.class, AndroidApiLevel.M))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ FieldSubject fieldSubject = inspector.clazz(Main.class).uniqueFieldWithName("FIELD");
+ assertThat(fieldSubject, isPresent());
+ assertEquals(
+ ApiLevel22.class.getTypeName(), fieldSubject.getField().getType().getTypeName());
+ })
+ .addRunClasspathClasses(ApiLevel22.class, ApiLevel23.class)
+ .run(parameters.getRuntime(), Main.class, Integer.toString(sdkInt))
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1),
+ runResult -> runResult.assertSuccessWithOutputLines("ApiLevel22"),
+ runResult -> runResult.assertSuccessWithOutputLines("null"));
+ }
+
+ public static class ApiLevel23 {}
+
+ public static class ApiLevel22 extends ApiLevel23 {
+
+ @Override
+ public String toString() {
+ return "ApiLevel22";
+ }
+ }
+
+ public static class Main {
+
+ public static ApiLevel23 FIELD;
+
+ public static void main(String[] args) {
+ int sdk = Integer.parseInt(args[0]);
+ if (Version.getSdkInt(sdk) >= 22) {
+ FIELD = new ApiLevel22();
+ }
+ System.out.println(FIELD);
+ }
+ }
+
+ public static class Version {
+
+ // -assumevalues ...
+ public static int getSdkInt(int sdk) {
+ return sdk;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java
new file mode 100644
index 0000000..1d17e5c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+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 ApiModelTypeStrengtheningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ boolean isTypeStrengtheningSafe =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.M);
+ int sdkInt = parameters.isCfRuntime() ? 0 : parameters.getApiLevel().getLevel();
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, Version.class)
+ .addLibraryClasses(ApiLevel22.class, ApiLevel23.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-assumevalues class " + Version.class.getTypeName() + " {",
+ " public static int getSdkInt(int) return " + sdkInt + "..42;",
+ "}")
+ .apply(setMockApiLevelForClass(ApiLevel22.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForClass(ApiLevel23.class, AndroidApiLevel.M))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ Class<?> expectedFieldType =
+ isTypeStrengtheningSafe ? ApiLevel23.class : ApiLevel22.class;
+ FieldSubject fieldSubject = inspector.clazz(Main.class).uniqueFieldWithName("FIELD");
+ assertThat(fieldSubject, isPresent());
+ assertEquals(
+ expectedFieldType.getTypeName(), fieldSubject.getField().getType().getTypeName());
+ })
+ .addRunClasspathClasses(ApiLevel22.class, ApiLevel23.class)
+ .run(parameters.getRuntime(), Main.class, Integer.toString(sdkInt))
+ .applyIf(
+ isTypeStrengtheningSafe,
+ runResult -> runResult.assertSuccessWithOutputLines("ApiLevel23"),
+ runResult -> runResult.assertSuccessWithOutputLines("null"));
+ }
+
+ public static class ApiLevel22 {}
+
+ public static class ApiLevel23 extends ApiLevel22 {
+
+ @Override
+ public String toString() {
+ return "ApiLevel23";
+ }
+ }
+
+ public static class Main {
+
+ public static ApiLevel22 FIELD;
+
+ public static void main(String[] args) {
+ int sdk = Integer.parseInt(args[0]);
+ if (Version.getSdkInt(sdk) >= 23) {
+ FIELD = new ApiLevel23();
+ }
+ System.out.println(FIELD);
+ }
+ }
+
+ public static class Version {
+
+ // -assumevalues ...
+ public static int getSdkInt(int sdk) {
+ return sdk;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
index e1f0d55..001c239 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.benchmarks.BenchmarkMethod;
import com.android.tools.r8.benchmarks.BenchmarkTarget;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.Retrace;
import com.android.tools.r8.retrace.RetraceCommand;
import com.google.common.collect.ImmutableList;
@@ -69,8 +70,12 @@
long start = System.nanoTime();
Retrace.run(
RetraceCommand.builder()
- .setProguardMapProducer(
- ProguardMapProducer.fromPath(dependencyRoot.resolve("r8lib.jar.map")))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromPath(
+ dependencyRoot.resolve("r8lib.jar.map")))
+ .build())
.setStackTrace(stackTrace)
.setRetracedStackTraceConsumer(retraced::addAll)
.build());
diff --git a/src/test/java/com/android/tools/r8/cf/InconsistentLocalTypeOnExceptionEdgeTest.java b/src/test/java/com/android/tools/r8/cf/InconsistentLocalTypeOnExceptionEdgeTest.java
new file mode 100644
index 0000000..9c97a2e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/InconsistentLocalTypeOnExceptionEdgeTest.java
@@ -0,0 +1,112 @@
+// 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;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.jasmin.JasminTestBase;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import java.util.List;
+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 InconsistentLocalTypeOnExceptionEdgeTest extends JasminTestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ List<byte[]> classFileData = getProgramClassFileData();
+ String main = "Main";
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(classFileData)
+ .run(parameters.getRuntime(), main)
+ .assertFailureWithErrorThatThrows(VerifyError.class);
+ } else {
+ try {
+ testForD8()
+ .addProgramClassFileData(classFileData)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ } catch (CompilationFailedException e) {
+ inspectCompilationFailedException(e);
+ }
+ }
+
+ try {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(classFileData)
+ .addKeepAllClassesRule()
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ allOf(
+ containsString(
+ "Unverifiable code in `void Main.main(java.lang.String[])`"),
+ containsString(
+ "Expected object at local index 0, but was top"))))));
+ fail("Expected compilation to fail");
+ } catch (CompilationFailedException e) {
+ inspectCompilationFailedException(e);
+ }
+ }
+
+ private List<byte[]> getProgramClassFileData() throws Exception {
+ JasminBuilder appBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = appBuilder.addClass("Main");
+ classBuilder.addStaticField("FIELD", "I");
+ classBuilder.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ "LabelTryStart:",
+ // At this throwing instruction we have locals=[0: java.lang.String[]].
+ " getstatic Main/FIELD I",
+ " istore 0",
+ " aconst_null",
+ // At this throwing instruction we have locals=[0: int].
+ " athrow",
+ "LabelTryEnd:",
+ "LabelCatch:",
+ // Unsafe attempt to read an object at local index 0.
+ " aload 0",
+ " invokestatic java/util/Arrays/toString([Ljava/lang/Object;)Ljava/lang/String;",
+ " pop",
+ " return",
+ ".catch java/lang/Throwable from LabelTryStart to LabelTryEnd using LabelCatch");
+ return appBuilder.buildClasses();
+ }
+
+ private void inspectCompilationFailedException(CompilationFailedException e) {
+ assertThat(
+ e.getCause().getMessage(),
+ containsString("Cannot constrain type: INT for value: v1 by constraint: OBJECT"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
index 792f793..b4b25c7 100644
--- a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
+++ b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
@@ -3,6 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8TestRunResult;
@@ -11,6 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
@@ -53,9 +59,19 @@
.addProgramClasses(TestClass.class, A.class)
.addKeepMainRule(TestClass.class)
.addDontWarn(B.class)
+ .allowDiagnosticWarningMessages()
.enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + TestClass.class.getTypeName()
+ + ".main(java.lang.String[])`")))))
.addRunClasspathFiles(getRuntimeClasspath())
.run(parameters.getRuntime(), TestClass.class);
if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 10ac394..f7ce233 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.Retrace;
import com.android.tools.r8.retrace.RetraceCommand;
import com.android.tools.r8.utils.FileUtils;
@@ -138,7 +139,10 @@
RetraceCommand retraceCommand =
RetraceCommand.builder()
.setStackTrace(StringUtils.splitLines(processResult.stderr))
- .setProguardMapProducer(ProguardMapProducer.fromPath(r8R8Release.getSecond()))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromPath(r8R8Release.getSecond()))
+ .build())
.setRetracedStackTraceConsumer(
retraced -> {
int expectedIndex = -1;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
index 3f0fac0..ad66194 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runners.Parameterized;
@@ -32,6 +33,8 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(Base.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addFeatureSplitRuntime()
.addFeatureSplit(Feature1Class1.class, Feature1Class2.class, Feature1Main.class)
.addFeatureSplit(Feature2Class.class, Feature2Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java
index 6f9a788..3c92a8d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java
@@ -4,11 +4,16 @@
package com.android.tools.r8.classmerging.horizontal;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import java.util.List;
import org.junit.Test;
@@ -41,8 +46,18 @@
.addHorizontallyMergedClassesInspector(
HorizontallyMergedClassesInspector::assertNoClassesMerged)
.applyIf(!enableOptimization, TestShrinkerBuilder::addDontOptimize)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + Main.class.getTypeName()
+ + ".dead()`")))))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("I");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java
index 8ee3476..984f431 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest.java
@@ -20,10 +20,10 @@
public class VirtualMethodMergingWithAbsentMethodAndSuperClassMergingTest extends TestBase {
@Parameter(0)
- public Class<?> upperMergeTarget;
+ public ClassWrapper upperMergeTarget;
@Parameter(1)
- public Class<?> lowerMergeTarget;
+ public ClassWrapper lowerMergeTarget;
@Parameter(2)
public TestParameters parameters;
@@ -31,11 +31,38 @@
@Parameters(name = "{2}, upper merge target: {0}, lower merge target: {1}")
public static List<Object[]> data() {
return buildParameters(
- ImmutableList.of(A.class, B.class),
- ImmutableList.of(C.class, D.class),
+ ImmutableList.of(ClassWrapper.create(A.class), ClassWrapper.create(B.class)),
+ ImmutableList.of(ClassWrapper.create(C.class), ClassWrapper.create(D.class)),
getTestParameters().withAllRuntimesAndApiLevels().build());
}
+ // Use a ClassWrapper to wrap the classes such that the string representation of the test do not
+ // exceed to many characters.
+ public static class ClassWrapper {
+ public final Class<?> clazz;
+
+ private ClassWrapper(Class<?> clazz) {
+ this.clazz = clazz;
+ }
+
+ public static ClassWrapper create(Class<?> clazz) {
+ return new ClassWrapper(clazz);
+ }
+
+ public String getTypeName() {
+ return clazz.getTypeName();
+ }
+
+ public boolean is(Class<?> clazz) {
+ return this.clazz == clazz;
+ }
+
+ @Override
+ public String toString() {
+ return clazz.getSimpleName();
+ }
+ }
+
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -69,11 +96,11 @@
inspector ->
inspector
.applyIf(
- upperMergeTarget == A.class,
+ upperMergeTarget.is(A.class),
i -> i.assertMergedInto(B.class, A.class),
i -> i.assertMergedInto(A.class, B.class))
.applyIf(
- lowerMergeTarget == C.class,
+ lowerMergeTarget.is(C.class),
i -> i.assertMergedInto(D.class, C.class),
i -> i.assertMergedInto(C.class, D.class))
.assertNoOtherClassesMerged())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index bfd9f97..cbbb43e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -858,7 +858,7 @@
ClassBuilder classBuilder = jasminBuilder.addClass(main);
classBuilder.addMainMethod(
".limit locals 1",
- ".limit stack 2",
+ ".limit stack 3",
// Instantiate B so that it is not merged into C.
"new classmerging/B",
"dup",
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 017c652..14a04dc 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.compilerapi.androidplatformbuild.AndroidPlatformBuildApiTest;
import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
import com.android.tools.r8.compilerapi.desugardependencies.DesugarDependenciesTest;
+import com.android.tools.r8.compilerapi.diagnostics.UnsupportedFeaturesDiagnosticApiTest;
import com.android.tools.r8.compilerapi.globalsynthetics.GlobalSyntheticsTest;
import com.android.tools.r8.compilerapi.inputdependencies.InputDependenciesTest;
import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
@@ -47,7 +48,8 @@
GlobalSyntheticsTest.ApiTest.class,
CommandLineParserTest.ApiTest.class,
EnableMissingLibraryApiModelingTest.ApiTest.class,
- AndroidPlatformBuildApiTest.ApiTest.class);
+ AndroidPlatformBuildApiTest.ApiTest.class,
+ UnsupportedFeaturesDiagnosticApiTest.ApiTest.class);
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/compilerapi/diagnostics/UnsupportedFeaturesDiagnosticApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/diagnostics/UnsupportedFeaturesDiagnosticApiTest.java
new file mode 100644
index 0000000..d6eeb25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/diagnostics/UnsupportedFeaturesDiagnosticApiTest.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.compilerapi.diagnostics;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.errors.InvokeCustomDiagnostic;
+import com.android.tools.r8.errors.UnsupportedFeatureDiagnostic;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class UnsupportedFeaturesDiagnosticApiTest extends CompilerApiTestRunner {
+
+ public UnsupportedFeaturesDiagnosticApiTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void test() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ test.run(
+ new InvokeCustomDiagnostic(Origin.unknown(), Position.UNKNOWN),
+ result -> {
+ assertEquals("invoke-custom @ 26", result);
+ });
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ public void run(Diagnostic diagnostic, Consumer<String> consumer) {
+ DiagnosticsHandler myHandler =
+ new DiagnosticsHandler() {
+ @Override
+ public void warning(Diagnostic warning) {
+ if (warning instanceof UnsupportedFeatureDiagnostic) {
+ UnsupportedFeatureDiagnostic unsupportedFeature =
+ (UnsupportedFeatureDiagnostic) warning;
+ String featureDescriptor = unsupportedFeature.getFeatureDescriptor();
+ int supportedApiLevel = unsupportedFeature.getSupportedApiLevel();
+ consumer.accept(featureDescriptor + " @ " + supportedApiLevel);
+ }
+ }
+ };
+ myHandler.warning(diagnostic);
+ }
+
+ @Test
+ public void test() throws Exception {
+ run(null, str -> {});
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
index 2e9abfb..9860a15 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -120,7 +120,6 @@
internalOptions -> {
if (parameters.isCfRuntime()) {
internalOptions.desugarState = DesugarState.ON;
- internalOptions.cfToCfDesugar = true;
}
});
if (parameters.isDexRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
index 59853ee..6c9fe50 100644
--- a/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/LambdaMissingInterfaceTest.java
@@ -4,12 +4,16 @@
package com.android.tools.r8.desugar;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -36,8 +40,18 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.addDontWarn(MissingInterface.class)
+ .allowDiagnosticWarningMessages()
.enableInliningAnnotations()
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + ClassWithLambda.class.getTypeName()
+ + ".callWithLambda()`")))))
.addRunClasspathClasses(MissingInterface.class)
.run(parameters.getRuntime(), Main.class)
// We allow for renaming if the class is missing
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
index 4022ff8..bcee062 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassTest.java
@@ -25,7 +25,8 @@
public class ConcurrentHashMapSubclassTest extends DesugaredLibraryTestBase {
private static final String EXPECTED_RESULT =
- StringUtils.lines("1.0", "2.0", "10.0", "1.0", "2.0", "10.0", "1.0", "2.0", "10.0");
+ StringUtils.lines(
+ "1.0", "2.0", "10.0", "1.0", "2.0", "10.0", "1.0", "2.0", "10.0", "1.0", "2.0", "10.0");
private final TestParameters parameters;
private final CompilationSpecification compilationSpecification;
@@ -64,11 +65,28 @@
@SuppressWarnings("unchecked")
static class Executor {
public static void main(String[] args) {
+ constructorTest();
directType();
classType();
itfType();
}
+ private static void constructorTest() {
+ Map map =
+ new ConcurrentHashMap<>(
+ 5, // initial capacity
+ 0.75f, // load factor (default)
+ 1 // concurrency level - only one thread will modify, others read only
+ );
+ map.put(1, 1.0);
+ map.putIfAbsent(2, 2.0);
+ map.putIfAbsent(2, 3.0);
+ map.putAll(example());
+ System.out.println(map.get(1));
+ System.out.println(map.get(2));
+ System.out.println(map.get(10));
+ }
+
static void itfType() {
Map map = new NullableConcurrentHashMap<Integer, Double>();
map.put(1, 1.0);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
index 6a169bf..fd59fc9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;
@@ -40,14 +42,16 @@
DESUGARED_JDK_8_LIB_JAR,
"desugar_jdk_libs.json",
AndroidApiLevel.L,
- LibraryDesugaringSpecification.JDK8_DESCRIPTOR);
+ LibraryDesugaringSpecification.JDK8_DESCRIPTOR,
+ LEGACY);
LibraryDesugaringSpecification jdk11InvalidLib =
new LibraryDesugaringSpecification(
"JDK11_INVALID_LIB",
ToolHelper.getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.L,
- LibraryDesugaringSpecification.JDK11_DESCRIPTOR);
+ LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
+ LATEST);
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
ImmutableList.of(jdk8InvalidLib, jdk11InvalidLib),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index b6be183..d90495c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.desugaredlibrary;
-import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -100,7 +100,8 @@
@Parameters(name = "{0}, spec: {1}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withNoneRuntime().build(), getJdk8Jdk11());
+ // TODO(b/236356665): Support JDK11 desugared lib.
+ return buildParameters(getTestParameters().withNoneRuntime().build(), ImmutableList.of(JDK8));
}
public ExtractWrapperTypesTest(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MaintainAndRewritePrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MaintainAndRewritePrefixTest.java
new file mode 100644
index 0000000..51b3660
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MaintainAndRewritePrefixTest.java
@@ -0,0 +1,84 @@
+// 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;
+
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.errors.CompilationError;
+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;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class MaintainAndRewritePrefixTest extends DesugaredLibraryTestBase implements Opcodes {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameters(name = "{0}, spec: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntime(Version.DEFAULT).withCfRuntime(CfVm.JDK11).build(),
+ LibraryDesugaringSpecification.getJdk8Jdk11());
+ }
+
+ public MaintainAndRewritePrefixTest(
+ TestParameters parameters, LibraryDesugaringSpecification libraryDesugaringSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ }
+
+ /**
+ * Add this library desugaring configuration:
+ * "library_flags": [
+ * {
+ * "rewrite_prefix": {"java.time.": "j$.time."},
+ * "maintain_prefix": ["java.time."],
+ * }
+ * ],
+ */
+ private static void specifyDesugaredLibrary(InternalOptions options) {
+ HumanRewritingFlags rewritingFlags =
+ HumanRewritingFlags.builder(options.reporter, Origin.unknown())
+ .putRewritePrefix("java.time.", "j$.time.")
+ .putMaintainPrefix("java.time.")
+ .build();
+ options.setDesugaredLibrarySpecification(
+ new HumanDesugaredLibrarySpecification(HumanTopLevelFlags.testing(), rewritingFlags, true));
+ }
+
+ @Test
+ public void test() throws Exception {
+ try {
+ testForL8(AndroidApiLevel.B, parameters.getBackend())
+ .apply(libraryDesugaringSpecification::configureL8TestBuilder)
+ .addOptionsModifier(MaintainAndRewritePrefixTest::specifyDesugaredLibrary)
+ .compile();
+ fail();
+ } catch (Exception e) {
+ Throwable cause = e.getCause();
+ org.junit.Assert.assertTrue(cause instanceof CompilationError);
+ CompilationError ce = (CompilationError) cause;
+ org.junit.Assert.assertTrue(
+ ce.getMessage()
+ .contains(
+ "The compilation cannot proceed because the desugared library specification"
+ + " contains ambiguous flags that the compiler cannot interpret"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index 66438e9..9c300c7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
@@ -76,14 +78,16 @@
DESUGARED_JDK_8_LIB_JAR,
"desugar_jdk_libs.json",
AndroidApiLevel.LATEST,
- LibraryDesugaringSpecification.JDK8_DESCRIPTOR);
+ LibraryDesugaringSpecification.JDK8_DESCRIPTOR,
+ LEGACY);
LibraryDesugaringSpecification jdk11MaxCompileSdk =
new LibraryDesugaringSpecification(
"JDK11_MAX",
ToolHelper.getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.LATEST,
- LibraryDesugaringSpecification.JDK11_DESCRIPTOR);
+ LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
+ LATEST);
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
ImmutableList.of(JDK8, JDK11, jdk8MaxCompileSdk, jdk11MaxCompileSdk),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 566b0a0..efee088 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -52,7 +54,7 @@
"JDK8_CL",
ImmutableSet.of(
DESUGARED_JDK_8_LIB_JAR,
- ToolHelper.DESUGAR_LIB_CONVERSIONS,
+ ToolHelper.getConvertedDesugaredLibConversions(LEGACY),
ToolHelper.getCoreLambdaStubs()),
JDK8.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.O)),
@@ -63,7 +65,7 @@
"JDK11_CL",
ImmutableSet.of(
ToolHelper.getUndesugaredJdk11LibJarForTesting(),
- ToolHelper.DESUGAR_LIB_CONVERSIONS,
+ ToolHelper.getConvertedDesugaredLibConversions(LATEST),
ToolHelper.getCoreLambdaStubs()),
JDK11.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.R)),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java
index da9064b..3afc61c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/ConcurrentHashMapFileSerializationTest.java
@@ -6,13 +6,19 @@
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
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.android.tools.r8.utils.codeinspector.FieldSubject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -64,11 +70,23 @@
.addKeepMainRule(Executor.class)
.noMinification()
.compile()
+ .inspectL8(this::assertVersionUID)
.withArt6Plus64BitsLib()
.run(parameters.getRuntime(), Executor.class)
.assertSuccessWithOutput(EXPECTED_RESULT);
}
+ private void assertVersionUID(CodeInspector inspector) {
+ ClassSubject mapClass = inspector.clazz("j$.util.concurrent.ConcurrentHashMap");
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ assertTrue(mapClass.isPresent());
+ FieldSubject serialVersionUID = mapClass.uniqueFieldWithName("serialVersionUID");
+ assertTrue(serialVersionUID.isPresent());
+ } else {
+ assertFalse(mapClass.isPresent());
+ }
+ }
+
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
static class Executor {
public static void main(String[] args) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java
index 8f02521..3c85888 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/MyMapFileSerializationTest.java
@@ -64,10 +64,20 @@
.noMinification()
.compile()
.withArt6Plus64BitsLib()
- .run(parameters.getRuntime(), Executor.class)
+ .run(parameters.getRuntime(), Executor.class, uniqueName())
.assertSuccessWithOutput(EXPECTED_RESULT);
}
+ private String uniqueName() {
+ return libraryDesugaringSpecification.toString()
+ + "_"
+ + compilationSpecification.toString()
+ + "_"
+ + parameters.getRuntime()
+ + "_"
+ + parameters.getApiLevel();
+ }
+
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
static class Executor {
@@ -76,7 +86,10 @@
MyMap<String, String> map = new MyMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
- File file = new File("testTemp");
+ // It seems the FileSystem is shared across multiple VM runs at least on some VMs.
+ // There is no easy way to create a temp file that works on all VM/configurations.
+ // We pass a unique string as parameter that we use for the file name.
+ File file = new File("test_" + args[0]);
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ChannelSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ChannelSetTest.java
index 193507a..cc4bbc3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ChannelSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ChannelSetTest.java
@@ -31,6 +31,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import org.junit.Test;
@@ -45,7 +46,7 @@
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
- private static final String EXPECTED_RESULT =
+ private static final String EXPECTED_RESULT_DESUGARING =
StringUtils.lines(
"bytes written: 11",
"String written: Hello World",
@@ -54,7 +55,17 @@
"bytes read: 11",
"String read: Hello World",
"unsupported");
- private static final String EXPECTED_RESULT_26 =
+ private static final String EXPECTED_RESULT_DESUGARING_PLATFORM_FILE_SYSTEM =
+ StringUtils.lines(
+ "bytes written: 11",
+ "String written: Hello World",
+ "bytes read: 11",
+ "String read: Hello World",
+ "bytes read: 11",
+ "String read: Hello World",
+ "bytes read: 11",
+ "String read: Hello World");
+ private static final String EXPECTED_RESULT_NO_DESUGARING =
StringUtils.lines(
"bytes written: 11",
"String written: Hello World",
@@ -97,7 +108,7 @@
}
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramClasses(TestClass.class)
.setCustomLibrarySpecification(
@@ -113,9 +124,12 @@
}
private String getExpectedResult() {
- return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)
- ? EXPECTED_RESULT_26
- : EXPECTED_RESULT;
+ if (!libraryDesugaringSpecification.hasNioFileDesugaring(parameters)) {
+ return EXPECTED_RESULT_NO_DESUGARING;
+ }
+ return libraryDesugaringSpecification.usesPlatformFileSystem(parameters)
+ ? EXPECTED_RESULT_DESUGARING_PLATFORM_FILE_SYSTEM
+ : EXPECTED_RESULT_DESUGARING;
}
public static class CustomLib {
@@ -163,12 +177,20 @@
pathWrapper.getFileSystem().provider().newFileChannel(pathWrapper, openOptions)) {
readFromChannel(channel, hello.length());
}
+ ExecutorService executor;
+ try {
+ executor = ForkJoinPool.commonPool();
+ } catch (Throwable t) {
+ // ForkJoinPool is not entirely supported below Android 5.
+ System.out.println("unsupported");
+ return;
+ }
try {
try (AsynchronousFileChannel channel =
pathWrapper
.getFileSystem()
.provider()
- .newAsynchronousFileChannel(pathWrapper, openOptions, ForkJoinPool.commonPool())) {
+ .newAsynchronousFileChannel(pathWrapper, openOptions, executor)) {
ByteBuffer byteBuffer = ByteBuffer.allocate(hello.length());
Future<Integer> readFuture = channel.read(byteBuffer, 0);
// We force the future to await here with get().
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ConversionConverter.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ConversionConverter.java
new file mode 100644
index 0000000..746a1d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ConversionConverter.java
@@ -0,0 +1,179 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
+
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.ClassFileTransformer;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.StreamUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.objectweb.asm.Opcodes;
+
+public class ConversionConverter {
+
+ private static final Map<String, String> JAVA_WRAP_CONVERT_OWNER = new HashMap<>();
+ private static final Map<String, String> J$_WRAP_CONVERT_OWNER = new HashMap<>();
+
+ static {
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/spi/FileSystemProvider",
+ "java/nio/file/spi/FileSystemProvider$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/spi/FileTypeDetector", "java/nio/file/spi/FileTypeDetector$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/StandardOpenOption", "java/nio/file/StandardOpenOption$EnumConversion");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/LinkOption", "java/nio/file/LinkOption$EnumConversion");
+ JAVA_WRAP_CONVERT_OWNER.put("j$/nio/file/Path", "java/nio/file/Path$Wrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/WatchEvent", "java/nio/file/WatchEvent$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/BasicFileAttributes",
+ "java/nio/file/attribute/BasicFileAttributes$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/BasicFileAttributeView",
+ "java/nio/file/attribute/BasicFileAttributeView$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/FileOwnerAttributeView",
+ "java/nio/file/attribute/FileOwnerAttributeView$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFileAttributes",
+ "java/nio/file/attribute/PosixFileAttributes$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFileAttributeView",
+ "java/nio/file/attribute/PosixFileAttributeView$VivifiedWrapper");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFilePermission",
+ "java/nio/file/attribute/PosixFilePermission$EnumConversion");
+ JAVA_WRAP_CONVERT_OWNER.put(
+ "j$/util/stream/Collector$Characteristics",
+ "java/util/stream/Collector$Characteristics$EnumConversion");
+
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/spi/FileSystemProvider", "java/nio/file/spi/FileSystemProvider$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/spi/FileTypeDetector", "java/nio/file/spi/FileTypeDetector$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/StandardOpenOption", "java/nio/file/StandardOpenOption$EnumConversion");
+ J$_WRAP_CONVERT_OWNER.put("j$/nio/file/LinkOption", "java/nio/file/LinkOption$EnumConversion");
+ J$_WRAP_CONVERT_OWNER.put("j$/nio/file/Path", "java/nio/file/Path$VivifiedWrapper");
+ J$_WRAP_CONVERT_OWNER.put("j$/nio/file/WatchEvent", "java/nio/file/WatchEvent$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/BasicFileAttributes",
+ "java/nio/file/attribute/BasicFileAttributes$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/BasicFileAttributeView",
+ "java/nio/file/attribute/BasicFileAttributeView$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/FileOwnerAttributeView",
+ "java/nio/file/attribute/FileOwnerAttributeView$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFileAttributes",
+ "java/nio/file/attribute/PosixFileAttributes$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFileAttributeView",
+ "java/nio/file/attribute/PosixFileAttributeView$Wrapper");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/nio/file/attribute/PosixFilePermission",
+ "java/nio/file/attribute/PosixFilePermission$EnumConversion");
+ J$_WRAP_CONVERT_OWNER.put(
+ "j$/util/stream/Collector$Characteristics",
+ "java/util/stream/Collector$Characteristics$EnumConversion");
+ }
+
+ public static Path convertJar(Path jar, CustomConversionVersion legacy) {
+ String fileName = jar.getFileName().toString();
+ String newFileName =
+ fileName.substring(0, fileName.length() - ".jar".length())
+ + (legacy == LEGACY ? "_legacy" : "")
+ + "_converted.jar";
+ Path convertedJar = jar.getParent().resolve(newFileName);
+ return internalConvert(jar, convertedJar, legacy);
+ }
+
+ private static synchronized Path internalConvert(
+ Path jar, Path convertedJar, CustomConversionVersion legacy) {
+ if (Files.exists(convertedJar)) {
+ return convertedJar;
+ }
+
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out =
+ new ZipOutputStream(
+ new BufferedOutputStream(Files.newOutputStream(convertedJar, options)))) {
+ new ConversionConverter().convert(jar, out, legacy);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return convertedJar;
+ }
+
+ private void convert(
+ Path desugaredLibraryFiles, ZipOutputStream out, CustomConversionVersion legacy)
+ throws IOException {
+ ZipUtils.iter(
+ desugaredLibraryFiles,
+ ((entry, input) -> {
+ if (!entry.getName().endsWith(".class")) {
+ return;
+ }
+ if (legacy == LEGACY
+ && (entry.getName().contains("nio.file") || entry.getName().contains("ApiFlips"))) {
+ return;
+ }
+ final byte[] bytes = StreamUtils.streamToByteArrayClose(input);
+ final byte[] rewrittenBytes =
+ transformInvoke(entry.getName().substring(0, entry.getName().length() - 6), bytes);
+ ZipUtils.writeToZipStream(out, entry.getName(), rewrittenBytes, ZipEntry.STORED);
+ }));
+ }
+
+ private byte[] transformInvoke(String descriptor, byte[] bytes) {
+ return ClassFileTransformer.create(bytes, Reference.classFromDescriptor(descriptor))
+ .addMethodTransformer(getMethodTransformer())
+ .transform();
+ }
+
+ private MethodTransformer getMethodTransformer() {
+ return new MethodTransformer() {
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("wrap_convert")) {
+ if (!JAVA_WRAP_CONVERT_OWNER.containsKey(owner)
+ || !J$_WRAP_CONVERT_OWNER.containsKey(owner)) {
+ throw new RuntimeException("Cannot transform wrap_convert method for " + owner);
+ }
+ if (owner.startsWith("java")) {
+ String newOwner = J$_WRAP_CONVERT_OWNER.get(owner);
+ super.visitMethodInsn(opcode, newOwner, "convert", descriptor, isInterface);
+ return;
+ } else if (owner.startsWith("j$")) {
+ String newOwner = JAVA_WRAP_CONVERT_OWNER.get(owner);
+ super.visitMethodInsn(opcode, newOwner, "convert", descriptor, isInterface);
+ return;
+ } else {
+ throw new RuntimeException("Cannot transform wrap_convert method for " + owner);
+ }
+ }
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ };
+ }
+}
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 e2ecec4..c9c9a31 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
@@ -6,6 +6,7 @@
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.StreamUtils;
import com.android.tools.r8.utils.ZipUtils;
@@ -74,7 +75,7 @@
}
private byte[] transformInvoke(String descriptor, byte[] bytes) {
- return transformer(bytes, Reference.classFromDescriptor(descriptor))
+ return ClassFileTransformer.create(bytes, Reference.classFromDescriptor(descriptor))
.addMethodTransformer(getMethodTransformer())
.transform();
}
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
index 7848f02..c40b5c4 100644
--- 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
@@ -74,7 +74,7 @@
}
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addInnerClasses(getClass())
.addProgramClasses(GoogleIcon.class)
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
index 1e4a04e..9508e8c 100644
--- 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
@@ -12,7 +12,6 @@
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -30,6 +29,7 @@
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,7 +39,7 @@
@RunWith(Parameterized.class)
public class FilesTest extends DesugaredLibraryTestBase {
- private static final String EXPECTED_RESULT =
+ private static final String EXPECTED_RESULT_DESUGARING_FILE_SYSTEM =
StringUtils.lines(
"bytes written: 11",
"String written: Hello World",
@@ -49,8 +49,9 @@
"String read: Hello World",
"null",
"true",
- "unsupported");
- private static final String EXPECTED_RESULT_24_26 =
+ "unsupported",
+ "j$.nio.file.attribute");
+ private static final String EXPECTED_RESULT_DESUGARING_FILE_SYSTEM_PLATFORM_CHANNEL =
StringUtils.lines(
"bytes written: 11",
"String written: Hello World",
@@ -60,8 +61,9 @@
"unsupported",
"null",
"true",
- "unsupported");
- private static final String EXPECTED_RESULT_26 =
+ "unsupported",
+ "j$.nio.file.attribute");
+ private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING =
StringUtils.lines(
"bytes written: 11",
"String written: Hello World",
@@ -71,7 +73,20 @@
"String read: Hello World",
"true",
"true",
- "true");
+ "true",
+ "j$.nio.file.attribute");
+ private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM =
+ StringUtils.lines(
+ "bytes written: 11",
+ "String written: Hello World",
+ "bytes read: 11",
+ "String read: Hello World",
+ "bytes read: 11",
+ "String read: Hello World",
+ "true",
+ "true",
+ "true",
+ "java.nio.file.attribute");
private final TestParameters parameters;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -100,16 +115,18 @@
}
private String getExpectedResult() {
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
- return EXPECTED_RESULT_26;
+ if (libraryDesugaringSpecification.usesPlatformFileSystem(parameters)) {
+ return libraryDesugaringSpecification.hasNioFileDesugaring(parameters)
+ ? EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING
+ : EXPECTED_RESULT_PLATFORM_FILE_SYSTEM;
}
- return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
- ? EXPECTED_RESULT_24_26
- : EXPECTED_RESULT;
+ return libraryDesugaringSpecification.hasNioChannelDesugaring(parameters)
+ ? EXPECTED_RESULT_DESUGARING_FILE_SYSTEM
+ : EXPECTED_RESULT_DESUGARING_FILE_SYSTEM_PLATFORM_CHANNEL;
}
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
@@ -126,6 +143,12 @@
readWriteThroughFilesAPI(path);
readThroughFileChannelAPI(path);
attributeAccess(path);
+ fspMethodsWithGeneric(path);
+ }
+
+ private static void fspMethodsWithGeneric(Path path) throws IOException {
+ Map<String, Object> mapping = Files.readAttributes(path, "lastModifiedTime");
+ System.out.println(mapping.values().iterator().next().getClass().getPackage().getName());
}
private static void attributeAccess(Path path) throws IOException {
@@ -146,7 +169,7 @@
try {
PosixFileAttributes posixAttributes = Files.readAttributes(path, PosixFileAttributes.class);
- if (attributes != null) {
+ if (posixAttributes != null) {
System.out.println(
posixAttributes.permissions().contains(PosixFilePermission.OWNER_READ));
} else {
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
index 542ac06..26e16bc 100644
--- 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
@@ -28,7 +28,14 @@
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
- private static final String EXPECTED_RESULT = StringUtils.lines("x.txt", "dir", "dir/x.txt", "/");
+ private static final String EXPECTED_RESULT_DESUGARING =
+ StringUtils.lines(
+ "x.txt", "dir", "dir/x.txt", "/", "class j$.desugar.sun.nio.fs.DesugarLinuxFileSystem");
+ private static final String EXPECTED_RESULT_DESUGARING_PLATFORM_FILE_SYSTEM =
+ StringUtils.lines(
+ "x.txt", "dir", "dir/x.txt", "/", "class j$.nio.file.FileSystem$VivifiedWrapper");
+ private static final String EXPECTED_RESULT_NO_DESUGARING =
+ StringUtils.lines("x.txt", "dir", "dir/x.txt", "/", "class sun.nio.fs.LinuxFileSystem");
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
@@ -47,13 +54,25 @@
this.compilationSpecification = compilationSpecification;
}
+ private String getExpectedResult() {
+ if (!libraryDesugaringSpecification.hasNioFileDesugaring(parameters)) {
+ return EXPECTED_RESULT_NO_DESUGARING;
+ }
+ return libraryDesugaringSpecification.usesPlatformFileSystem(parameters)
+ ? EXPECTED_RESULT_DESUGARING_PLATFORM_FILE_SYSTEM
+ : EXPECTED_RESULT_DESUGARING;
+ }
+
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addL8KeepRules("-keepnames class j$.desugar.sun.nio.fs.**")
+ .addL8KeepRules("-keepnames class j$.nio.file.FileSystem**")
.addInnerClasses(PathTest.class)
.addKeepMainRule(TestClass.class)
+ .compile()
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .assertSuccessWithOutput(getExpectedResult());
}
public static class TestClass {
@@ -67,6 +86,7 @@
Path resolve = path2.resolve(path1);
System.out.println(resolve);
System.out.println(resolve.getFileSystem().getSeparator());
+ System.out.println(resolve.getFileSystem().getClass());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java
new file mode 100644
index 0000000..56bfcd6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.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.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CustomLibrarySpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+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 StreamCollectorCharacteristicsTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT = StringUtils.lines("IDENTITY_FINISH");
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED),
+ ImmutableList.of(JDK11),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public StreamCollectorCharacteristicsTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .setCustomLibrarySpecification(
+ new CustomLibrarySpecification(CustomLib.class, MIN_SUPPORTED))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ public static class CustomLib {
+ public static Collector<?, ?, ?> getCollector() {
+ return Collectors.toList();
+ }
+ }
+
+ public static class TestClass {
+ public static void main(String[] args) {
+ System.out.println(CustomLib.getCollector().characteristics().iterator().next().toString());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
index 5cb8e57..8168e33 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
@@ -136,7 +136,6 @@
"WatchServiceBasic",
"WatchServiceFileTreeModifier",
"WatchServiceDeleteInterference",
- "WatchServiceMayFlies",
"WatchServiceLotsOfCancels",
"WatchServiceSensitivityModifier");
private static final List<String> FAILING_MAIN_TESTS =
@@ -157,6 +156,7 @@
"FilesTemporaryFiles",
"FilesCheckPermissions",
"FilesMisc",
+ "WatchServiceMayFlies", // Works but longest to run by far.
"WatchServiceWithSecurityManager",
"WatchServiceUpdateInterference",
"WatchServiceLotsOfCloses");
@@ -272,10 +272,12 @@
.compile()
.withArt6Plus64BitsLib();
int success = 0;
+ int failures = 0;
for (String mainTestClass : SUCCESSFUL_MAIN_TESTS) {
SingleTestRunResult<?> run = compileResult.run(parameters.getRuntime(), mainTestClass);
if (run.getExitCode() != 0) {
System.out.println("Main Fail " + mainTestClass);
+ failures++;
} else {
success++;
}
@@ -286,6 +288,7 @@
parameters.getRuntime(), "TestNGMainRunner", verbosity, testNGTestClass);
if (!result.getStdOut().contains(StringUtils.lines(testNGTestClass + ": SUCCESS"))) {
System.out.println("TestNG Fail " + testNGTestClass);
+ failures++;
} else {
success++;
}
@@ -293,7 +296,9 @@
// TODO(b/234689867): Understand and fix these issues.
// Most issues seem to come from the missing secure.properties file. This file is not accessed
// in all tests on all API levels, hence a different number of failures on each level.
- assertTrue(success >= 15);
+ System.out.println("Successes :" + success + "; failures " + failures);
+ assertTrue(success >= 11);
+ assertTrue(failures <= 20);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index b9ca3f1..5a2c8b0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -44,6 +44,7 @@
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
private final TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> builder;
+ private String l8ExtraKeepRules = "";
private Consumer<InternalOptions> l8OptionModifier = ConsumerUtils.emptyConsumer();
private boolean l8FinalPrefixVerification = true;
@@ -205,6 +206,13 @@
return this;
}
+ public DesugaredLibraryTestBuilder<T> addL8KeepRules(String keepRules) {
+ if (compilationSpecification.isL8Shrink()) {
+ l8ExtraKeepRules += keepRules + "\n";
+ }
+ return this;
+ }
+
public DesugaredLibraryTestBuilder<T> addKeepClassAndMembersRules(Class<?>... clazz) {
withR8TestBuilder(b -> b.addKeepClassAndMembersRules(clazz));
return this;
@@ -348,7 +356,7 @@
L8TestCompileResult nonShrunk =
test.testForL8(parameters.getApiLevel(), Backend.CF)
.apply(libraryDesugaringSpecification::configureL8TestBuilder)
- .apply(this::configure)
+ .apply(b -> configure(b, Backend.CF))
.compile();
String keepRules =
collectKeepRulesWithTraceReferences(compile.writeToZip(), nonShrunk.writeToZip());
@@ -362,13 +370,16 @@
b ->
libraryDesugaringSpecification.configureL8TestBuilder(
b, compilationSpecification.isL8Shrink(), keepRule))
- .apply(this::configure)
+ .apply(b -> configure(b, parameters.getBackend()))
.compile();
}
- private void configure(L8TestBuilder l8Builder) {
+ private void configure(L8TestBuilder l8Builder, Backend backend) {
l8Builder
.applyIf(!l8FinalPrefixVerification, L8TestBuilder::ignoreFinalPrefixVerification)
+ .applyIf(
+ compilationSpecification.isL8Shrink() && !backend.isCf() && !l8ExtraKeepRules.isEmpty(),
+ b -> b.addKeepRules(l8ExtraKeepRules))
.addOptionsModifier(l8OptionModifier);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 539ad9a..143a52d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -7,10 +7,13 @@
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.ToolHelper.DESUGARED_LIB_RELEASES_DIR;
import static com.android.tools.r8.ToolHelper.getUndesugaredJdk11LibJarForTesting;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
import com.android.tools.r8.L8TestBuilder;
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.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -73,6 +76,11 @@
}
}
+ public enum CustomConversionVersion {
+ LEGACY,
+ LATEST
+ }
+
// Main head specifications.
public static LibraryDesugaringSpecification JDK8 =
new LibraryDesugaringSpecification(
@@ -80,28 +88,32 @@
DESUGARED_JDK_8_LIB_JAR,
"desugar_jdk_libs.json",
AndroidApiLevel.P,
- JDK8_DESCRIPTOR);
+ JDK8_DESCRIPTOR,
+ LEGACY);
public static LibraryDesugaringSpecification JDK11 =
new LibraryDesugaringSpecification(
"JDK11",
getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.R,
- JDK11_DESCRIPTOR);
+ JDK11_DESCRIPTOR,
+ LATEST);
public static LibraryDesugaringSpecification JDK11_MINIMAL =
new LibraryDesugaringSpecification(
"JDK11_MINIMAL",
getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs_minimal.json",
AndroidApiLevel.R,
- EMPTY_DESCRIPTOR_24);
+ EMPTY_DESCRIPTOR_24,
+ LATEST);
public static LibraryDesugaringSpecification JDK11_PATH =
new LibraryDesugaringSpecification(
"JDK11_PATH",
getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs_path.json",
AndroidApiLevel.R,
- JDK11_PATH_DESCRIPTOR);
+ JDK11_PATH_DESCRIPTOR,
+ LATEST);
// Legacy specifications.
public static LibraryDesugaringSpecification JDK11_PATH_ALTERNATIVE_3 =
@@ -110,14 +122,16 @@
getUndesugaredJdk11LibJarForTesting(),
"jdk11/desugar_jdk_libs_path_alternative_3.json",
AndroidApiLevel.R,
- JDK11_PATH_DESCRIPTOR);
+ JDK11_PATH_DESCRIPTOR,
+ LATEST);
public static LibraryDesugaringSpecification JDK11_CHM_ONLY =
new LibraryDesugaringSpecification(
"JDK11_CHM_ONLY",
getUndesugaredJdk11LibJarForTesting(),
"jdk11/chm_only_desugar_jdk_libs.json",
AndroidApiLevel.R,
- EMPTY_DESCRIPTOR_24);
+ EMPTY_DESCRIPTOR_24,
+ LATEST);
public static LibraryDesugaringSpecification JDK11_LEGACY =
new LibraryDesugaringSpecification(
"JDK11_LEGACY",
@@ -125,7 +139,8 @@
DESUGARED_JDK_11_LIB_JAR,
"jdk11/desugar_jdk_libs_legacy.json",
AndroidApiLevel.R,
- JDK11_LEGACY_DESCRIPTOR);
+ JDK11_LEGACY_DESCRIPTOR,
+ LEGACY);
public static final LibraryDesugaringSpecification RELEASED_1_0_9 =
new LibraryDesugaringSpecification("1.0.9", AndroidApiLevel.P);
public static final LibraryDesugaringSpecification RELEASED_1_0_10 =
@@ -149,10 +164,11 @@
Path desugarJdkLibs,
String specificationPath,
AndroidApiLevel androidJarLevel,
- Descriptor descriptor) {
+ Descriptor descriptor,
+ CustomConversionVersion legacy) {
this(
name,
- ImmutableSet.of(desugarJdkLibs, ToolHelper.DESUGAR_LIB_CONVERSIONS),
+ ImmutableSet.of(desugarJdkLibs, ToolHelper.getConvertedDesugaredLibConversions(legacy)),
Paths.get("src/library_desugar/" + specificationPath),
ImmutableSet.of(ToolHelper.getAndroidJar(androidJarLevel)),
descriptor,
@@ -254,6 +270,14 @@
return parameters.getApiLevel().getLevel() < descriptor.getNioFileDesugaring();
}
+ public boolean hasNioChannelDesugaring(TestParameters parameters) {
+ return hasNioFileDesugaring(parameters) && parameters.getApiLevel().getLevel() < 24;
+ }
+
+ public boolean usesPlatformFileSystem(TestParameters parameters) {
+ return parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0);
+ }
+
public boolean hasAnyDesugaring(TestParameters parameters) {
return hasAnyDesugaring(parameters.getApiLevel());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 6fa1514..04e2842 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -74,7 +74,6 @@
builder.addOptionsModification(
options -> {
options.desugarState = DesugarState.ON;
- options.cfToCfDesugar = true;
}))
.compile()
.inspect(inspector -> assertNests(inspector, desugar))
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
index 6e19e01..46bfd05 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
@@ -7,14 +7,12 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.util.function.Function;
@@ -34,7 +32,7 @@
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
- private static final String EXPECTED_OUTPUT = StringUtils.lines("8");
+ private static final String EXPECTED_OUTPUT = "8";
private void inspect(CodeInspector inspector) {
assertTrue(
@@ -47,6 +45,10 @@
.noneMatch(name -> name.endsWith("$-CC")));
}
+ private String getExpectedOutputForApiCheck() {
+ return parameters.getApiLevel().isLessThan(AndroidApiLevel.N) ? "No call" : EXPECTED_OUTPUT;
+ }
+
@Test
public void testDesugaring() throws Exception {
testForD8(parameters.getBackend())
@@ -64,7 +66,7 @@
.maxSupportedApiLevel()
.isLessThan(AndroidApiLevel.N),
r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ r -> r.assertSuccessWithOutputLines(EXPECTED_OUTPUT));
}
@Test
@@ -73,59 +75,22 @@
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
- .addAndroidBuildVersion(parameters.getRuntime().asDex().maxSupportedApiLevel())
+ .addAndroidBuildVersion(parameters.getApiLevel())
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .asDex()
- .maxSupportedApiLevel()
- .isLessThan(AndroidApiLevel.N),
- r -> r.assertSuccessWithOutputLines("No call"),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ .assertSuccessWithOutputLines(getExpectedOutputForApiCheck());
}
@Test
public void testR8() throws Exception {
- try {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(TestClass.class)
- .compile()
- // .inspect(this::inspect)
- .run(parameters.getRuntime(), TestClass.class)
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .asDex()
- .maxSupportedApiLevel()
- .isLessThan(AndroidApiLevel.N),
- r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
- } catch (CompilationFailedException e) {
- // TODO(b/235184674): Fix this.
- assertTrue(parameters.isCfRuntime());
- }
- }
-
- // TODO(b/235184674): Fix this.
- @Test(expected = CompilationFailedException.class)
- public void testR8WithApiLevelCheck() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.setMinApi(parameters.getApiLevel())
- .addKeepMainRule(TestClassWithApiLevelCheck.class)
- .compile()
- .inspect(this::inspect)
- .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.isDexRuntime()
&& parameters
@@ -134,7 +99,19 @@
.maxSupportedApiLevel()
.isLessThan(AndroidApiLevel.N),
r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ r -> r.assertSuccessWithOutputLines(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8WithApiLevelCheck() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClassWithApiLevelCheck.class)
+ .addAndroidBuildVersion(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+ .assertSuccessWithOutputLines(getExpectedOutputForApiCheck());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DevirtualizationAcrossFeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/DevirtualizationAcrossFeatureSplitTest.java
index 538f764..0553efe 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DevirtualizationAcrossFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DevirtualizationAcrossFeatureSplitTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -29,6 +30,8 @@
public void test() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class, BaseInterface.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addFeatureSplitRuntime()
.addFeatureSplit(FeatureMain.class, BaseInterfaceImpl.class)
.addKeepFeatureMainRules(FeatureMain.class)
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 71e7df5..f56464c 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterFieldTypeStrengtheningTest.java
@@ -5,22 +5,16 @@
package com.android.tools.r8.dexsplitter;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.google.common.collect.ImmutableSet;
@@ -67,7 +61,10 @@
Object.class.getTypeName(),
fieldSubject.getField().getType().getTypeName());
}),
- ThrowableConsumer.empty());
+ // Link against android.jar that contains ReflectiveOperationException.
+ testBuilder ->
+ testBuilder.addLibraryFiles(
+ parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K)));
assertEquals(processResult.exitCode, 0);
assertEquals(processResult.stdout, EXPECTED);
}
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 53af2b6..dfc4327 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
@@ -4,28 +4,20 @@
package com.android.tools.r8.dexsplitter;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -52,7 +44,11 @@
assumeTrue(parameters.isDexRuntime());
ThrowableConsumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
- r8FullTestBuilder.enableNoVerticalClassMergingAnnotations().noMinification();
+ r8FullTestBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification();
ProcessResult processResult =
testR8Splitter(
parameters,
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 a2e1b1d..ad8ab38 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
@@ -4,24 +4,17 @@
package com.android.tools.r8.dexsplitter;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import org.junit.Test;
@@ -56,7 +49,11 @@
FeatureClass.class,
ThrowableConsumer.empty(),
testBuilder ->
- testBuilder.enableInliningAnnotations().addDontObfuscate(FeatureEnum.class));
+ testBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
+ .addDontObfuscate(FeatureEnum.class)
+ .enableInliningAnnotations());
assertEquals(processResult.exitCode, 0);
assertEquals(processResult.stdout, EXPECTED);
}
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 a7d137f..c83e290 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.dexsplitter;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -15,17 +12,14 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -52,7 +46,11 @@
assumeTrue(parameters.isDexRuntime());
ThrowableConsumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
- r8FullTestBuilder.enableNoVerticalClassMergingAnnotations().noMinification();
+ r8FullTestBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification();
ProcessResult processResult =
testR8Splitter(
parameters,
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
index b5940d4..b2d8fe8 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -38,7 +39,7 @@
@RunWith(Parameterized.class)
public class R8FeatureSplitTest extends SplitterTestBase {
- private static String EXPECTED = "Hello world";
+ private static final String EXPECTED = "Hello world";
@Parameters(name = "{0}")
public static TestParametersCollection params() {
@@ -62,7 +63,7 @@
public void simpleApiTest() throws CompilationFailedException, IOException, ExecutionException {
testForR8(parameters.getBackend())
.addProgramClasses(HelloWorld.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.addFeatureSplit(R8FeatureSplitTest::emptySplitProvider)
.addKeepMainRule(HelloWorld.class)
.compile()
@@ -129,6 +130,8 @@
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.setMinApi(parameters.getApiLevel())
.addFeatureSplit(
builder ->
@@ -253,6 +256,8 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.setMinApi(parameters.getApiLevel())
.addFeatureSplit(FeatureClass.class)
.addFeatureSplit(FeatureClass2.class)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
index 496c3d3..6576720 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
@@ -50,7 +51,11 @@
assumeTrue(parameters.isDexRuntime());
ThrowableConsumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
- r8FullTestBuilder.enableNoVerticalClassMergingAnnotations().noMinification();
+ r8FullTestBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification();
ThrowableConsumer<R8TestCompileResult> ensureInlined =
r8TestCompileResult -> {
// Ensure that isEarly from BaseUtilClass is inlined into the feature
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/StaticClassMergingInFeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/StaticClassMergingInFeatureSplitTest.java
index a1c961e..78e7738 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/StaticClassMergingInFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/StaticClassMergingInFeatureSplitTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +38,8 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(BaseClassA.class, BaseClassB.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addFeatureSplitRuntime()
.addFeatureSplit(Feature1Main.class, Feature1ClassA.class, Feature1ClassB.class)
.addFeatureSplit(Feature2Main.class, Feature2ClassA.class, Feature2ClassB.class)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index 3e86cc9..3dd0858 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableSet;
@@ -93,6 +94,8 @@
private void configure(R8FullTestBuilder testBuilder) throws NoSuchMethodException {
testBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addKeepMethodRules(
Reference.methodFromMethod(
BaseSuperClass.class.getDeclaredMethod(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingAcrossFeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingAcrossFeatureSplitTest.java
index 4ff8f4b..933c3c7 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingAcrossFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingAcrossFeatureSplitTest.java
@@ -12,9 +12,7 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.dexsplitter.VerticalClassMergingInFeatureSplitTest.BaseClass;
-import com.android.tools.r8.dexsplitter.VerticalClassMergingInFeatureSplitTest.Feature1Class;
-import com.android.tools.r8.dexsplitter.VerticalClassMergingInFeatureSplitTest.Feature2Class;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +37,8 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addFeatureSplitRuntime()
.addFeatureSplit(Feature1Class.class)
.addFeatureSplit(Feature2Main.class, Feature2Class.class)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingInFeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingInFeatureSplitTest.java
index 13234ef..4e7e2d0 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingInFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/VerticalClassMergingInFeatureSplitTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,6 +44,8 @@
.addFeatureSplit(
Feature2Main.class, Feature2Class.class, Feature2ClassWithSameFeatureSubclass.class)
.addKeepFeatureMainRules(Feature1Main.class, Feature2Main.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java b/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
similarity index 66%
rename from src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java
rename to src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
index 66f976b..8133dc2 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/UnsupportedFeaturesDiagnosticsTest.java
@@ -4,20 +4,26 @@
package com.android.tools.r8.diagnostics;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.startsWith;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsMatcher;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.UnsupportedFeatureDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-// TODO(b/154778581): Extend these tests with typed diagnostics and improved information.
@RunWith(Parameterized.class)
-public class ApiLevelDiagnosticTest extends TestBase {
+public class UnsupportedFeaturesDiagnosticsTest extends TestBase {
// Hard coded messages in AGP. See D8DexArchiveBuilder.
@@ -35,7 +41,30 @@
return getTestParameters().withNoneRuntime().build();
}
- public ApiLevelDiagnosticTest(TestParameters parameters) {
+ private static class FeatureMatcher extends DiagnosticsMatcher {
+ private final String descriptor;
+
+ public FeatureMatcher(String descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ protected boolean eval(Diagnostic diagnostic) {
+ return ((UnsupportedFeatureDiagnostic) diagnostic).getFeatureDescriptor().equals(descriptor);
+ }
+
+ @Override
+ protected void explain(Description description) {
+ description.appendText("feature ").appendText(descriptor);
+ }
+ }
+
+ public static Matcher<Diagnostic> matches(String descriptor) {
+ return allOf(
+ diagnosticType(UnsupportedFeatureDiagnostic.class), new FeatureMatcher(descriptor));
+ }
+
+ public UnsupportedFeaturesDiagnosticsTest(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@@ -49,7 +78,10 @@
diagnostics -> {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticMessage(startsWith(AGP_INVOKE_CUSTOM)));
+ .assertErrorsMatch(
+ allOf(
+ matches("invoke-custom"),
+ diagnosticMessage(startsWith(AGP_INVOKE_CUSTOM))));
});
}
@@ -63,7 +95,10 @@
diagnostics -> {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticMessage(startsWith(AGP_DEFAULT_INTERFACE_METHOD)));
+ .assertErrorsMatch(
+ allOf(
+ matches("default-interface-method"),
+ diagnosticMessage(startsWith(AGP_DEFAULT_INTERFACE_METHOD))));
});
}
@@ -77,7 +112,10 @@
diagnostics -> {
diagnostics
.assertOnlyErrors()
- .assertErrorsMatch(diagnosticMessage(startsWith(AGP_STATIC_INTERFACE_METHOD)));
+ .assertErrorsMatch(
+ allOf(
+ matches("static-interface-method"),
+ diagnosticMessage(startsWith(AGP_STATIC_INTERFACE_METHOD))));
});
}
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
index cb43c01..5b2431d 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
@@ -15,6 +18,7 @@
import com.android.tools.r8.transformers.ClassTransformer;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -147,7 +151,18 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(MainClassFailing.class)
.addOptionsModification(o -> o.testing.allowTypeErrors = true)
+ .allowDiagnosticWarningMessages()
.enableNoMethodStaticizingAnnotations()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + InvokerClass.class.getTypeName()
+ + ".invokeSubLevel2MethodOnSubClassOfInvokerClass()`")))))
.run(parameters.getRuntime(), MainClassFailing.class)
.apply(r -> checkNonVerifyingResult(r, true));
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java
new file mode 100644
index 0000000..c40df02
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java
@@ -0,0 +1,72 @@
+// 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.graph.invokespecial;
+
+import static com.android.tools.r8.graph.invokespecial.InvokeSpecialToEnumUnboxedMethodTest.MyEnum.TEST_1;
+import static com.android.tools.r8.graph.invokespecial.InvokeSpecialToEnumUnboxedMethodTest.MyEnum.TEST_2;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DiagnosticsMatcher;
+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;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialToEnumUnboxedMethodTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ // TODO(b/235817866): Should not have invalid assert.
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ DiagnosticsMatcher.diagnosticException(AssertionError.class))));
+ }
+
+ public enum MyEnum {
+ TEST_1("Foo"),
+ TEST_2("Bar");
+
+ private final String str;
+
+ MyEnum(String str) {
+ this.str = str;
+ }
+
+ @NeverInline
+ private String getStr() {
+ return str;
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println((args.length == 0 ? TEST_1 : TEST_2).getStr());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
index b8dd4fc..043dcb7 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
@@ -4,12 +4,17 @@
package com.android.tools.r8.graph.invokespecial;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.List;
@@ -49,9 +54,12 @@
.assertFailureWithErrorThatThrowsIf(!isExpectedToSucceedWithD8(), VerifyError.class);
}
+ private boolean isExpectedToSucceedWithJvm() {
+ return holder == C.class;
+ }
+
private boolean isExpectedToSucceedWithD8() {
- if (holder == C.class) {
- // Should always succeed.
+ if (isExpectedToSucceedWithJvm()) {
return true;
}
// TODO(b/144410139): Consider making this a compilation failure instead.
@@ -72,7 +80,22 @@
.addProgramClasses(EmptySubC.class, C.class, D.class, Main.class)
.addProgramClassFileData(getClassBWithTransformedInvoked(holder))
.addKeepMainRule(Main.class)
+ .allowDiagnosticWarningMessages(!isExpectedToSucceedWithJvm())
.setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (!isExpectedToSucceedWithJvm()) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + "void "
+ + B.class.getTypeName()
+ + ".callPrint()`"))));
+ }
+ })
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("D");
}
diff --git a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
index 183031e..ddd9bd4 100644
--- a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
+++ b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
@@ -3,14 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.utils.codeinspector.Matchers.proguardConfigurationRuleDoesNotMatch;
import static com.android.tools.r8.utils.codeinspector.Matchers.typeVariableNotInScope;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
@@ -48,10 +53,20 @@
.allowUnusedProguardConfigurationRules()
.allowUnnecessaryDontWarnWildcards()
.setMinApi(AndroidApiLevel.N)
- .allowDiagnosticInfoMessages()
+ .allowDiagnosticMessages()
.compileWithExpectedDiagnostics(
diagnostics ->
- diagnostics.assertAllInfosMatch(
- anyOf(typeVariableNotInScope(), proguardConfigurationRuleDoesNotMatch())));
+ diagnostics
+ .assertAllInfosMatch(
+ anyOf(typeVariableNotInScope(), proguardConfigurationRuleDoesNotMatch()))
+ .assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + "void zzz.com.facebook.litho.ComponentHost"
+ + ".refreshAccessibilityDelegatesIfNeeded(boolean)`"))))
+ .assertNoErrors());
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
index 4ad8b97..60f6fa7 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -127,6 +127,23 @@
anyOf(
containsString("Expected stack map table for method with non-linear control flow."),
containsString("Ignoring option: -outjars"),
+ containsString(
+ "Unverifiable code in `"
+ + "java.net.Socket com.google.android.gms.org.conscrypt."
+ + "KitKatPlatformOpenSSLSocketAdapterFactory.wrap("
+ + "com.google.android.gms.org.conscrypt.OpenSSLSocketImpl)`"),
+ containsString(
+ "Unverifiable code in `"
+ + "java.net.Socket com.google.android.gms.org.conscrypt."
+ + "PreKitKatPlatformOpenSSLSocketAdapterFactory.wrap("
+ + "com.google.android.gms.org.conscrypt.OpenSSLSocketImpl)`"),
+ containsString(
+ "Unverifiable code in `"
+ + "android.content.pm.PackageStats com.google.android.libraries.performance"
+ + ".primes.metriccapture.PackageStatsCapture"
+ + ".getPackageStatsUsingInternalAPI(android.content.Context, long, "
+ + "com.google.android.libraries.performance.primes.metriccapture."
+ + "PackageStatsCapture$PackageStatsInvocation[])`"),
allOf(
startsWith(
"Rule matches the static final field "
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10ProguardMapReaderTest.java
index 2c2742a..58bef78 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10ProguardMapReaderTest.java
@@ -17,8 +17,9 @@
@Test
public void roundTripTestGmsCoreV10() throws IOException {
Path map = Paths.get(APP_DIR).resolve(PG_MAP);
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(map);
- ClassNameMapper secondMapper = ClassNameMapper.mapperFromString(firstMapper.toString());
+ ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(map).sorted();
+ ClassNameMapper secondMapper =
+ ClassNameMapper.mapperFromString(firstMapper.toString()).sorted();
Assert.assertEquals(firstMapper, secondMapper);
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index a6277d6..b09cb00 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -205,6 +205,25 @@
anyOf(
containsString("Ignoring option: -optimizations"),
containsString("Proguard configuration rule does not match anything")))
- .assertAllWarningMessagesMatch(containsString("Ignoring option: -outjars"));
+ .assertAllWarningMessagesMatch(
+ anyOf(
+ containsString("Ignoring option: -outjars"),
+ containsString(
+ "Unverifiable code in `"
+ + "java.net.Socket com.google.android.gms.org.conscrypt."
+ + "KitKatPlatformOpenSSLSocketAdapterFactory.wrap("
+ + "com.google.android.gms.org.conscrypt.OpenSSLSocketImpl)`"),
+ containsString(
+ "Unverifiable code in `"
+ + "java.net.Socket com.google.android.gms.org.conscrypt."
+ + "PreKitKatPlatformOpenSSLSocketAdapterFactory.wrap("
+ + "com.google.android.gms.org.conscrypt.OpenSSLSocketImpl)`"),
+ containsString(
+ "Unverifiable code in `"
+ + "android.content.pm.PackageStats com.google.android.libraries.performance"
+ + ".primes.metriccapture.PackageStatsCapture"
+ + ".getPackageStatsUsingInternalAPI(android.content.Context, long, "
+ + "com.google.android.libraries.performance.primes.metriccapture."
+ + "PackageStatsCapture$PackageStatsInvocation[])`")));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/Regression127524985.java b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
index a39814c..009c587 100644
--- a/src/test/java/com/android/tools/r8/internal/Regression127524985.java
+++ b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
@@ -3,10 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.internal;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeTrue;
+
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 com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
@@ -36,25 +43,56 @@
}
@Test
- public void test() throws Throwable {
- if (parameters.isCfRuntime()) {
- testForJvm()
- .addClasspath(JAR)
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(EXPECTED);
- }
- (parameters.isDexRuntime()
- ? testForD8()
- : testForR8(parameters.getBackend())
- .debug()
- .noTreeShaking()
- .noMinification()
- .addKeepAllAttributes()
- .addKeepRules("-dontwarn"))
+ public void testJvm() throws Throwable {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addClasspath(JAR)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Throwable {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
.addProgramFiles(JAR)
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED);
}
+
+ @Test
+ public void testR8Cf() throws Throwable {
+ assumeTrue(parameters.isCfRuntime());
+ testForR8(parameters.getBackend())
+ .debug()
+ .noTreeShaking()
+ .noMinification()
+ .addKeepAllAttributes()
+ .addKeepRules("-dontwarn")
+ .allowDiagnosticWarningMessages()
+ .addProgramFiles(JAR)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + "void com.google.protobuf.contrib.android."
+ + "ProtoParsers$InternalDontUse.<clinit>()`"))),
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + "void com.google.protobuf.contrib.android.ProtoParsers"
+ + ".put(android.os.Bundle, java.lang.String, "
+ + "com.google.protobuf.MessageLite)`")))))
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(EXPECTED);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
index d283ee6..0e5ce56 100644
--- a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.internal.retrace.stacktraces.FinskyStackTrace;
import com.android.tools.r8.internal.retrace.stacktraces.VelvetStackTrace;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.Retrace;
import com.android.tools.r8.retrace.RetraceCommand;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
@@ -199,7 +200,11 @@
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .build())
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
.setRegularExpression(regularExpression)
.setRetracedStackTraceConsumer(
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 16e570a..81f92c3 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -126,7 +126,7 @@
if (instruction.isFrame()) {
return instruction
.asFrame()
- .map(
+ .mapReferenceTypes(
type ->
(type.getTypeName().endsWith("$UnsafeStub"))
? itemFactory.createType("Lsun/misc/Unsafe;")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java
new file mode 100644
index 0000000..7d99b55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationMonitorTest.java
@@ -0,0 +1,80 @@
+// 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.canonicalization;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a regression test for b/235319568 */
+@RunWith(Parameterized.class)
+public class ConstClassCanonicalizationMonitorTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Main.class)
+ .enableInliningAnnotations()
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .addOptionsModification(
+ options -> {
+ options.proguardMapConsumer = null;
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World!")
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(Main.class);
+ assertThat(clazz, isPresent());
+ MethodSubject testSubject = clazz.uniqueMethodWithName("test");
+ assertThat(testSubject, isPresent());
+ Optional<InstructionSubject> insertedMonitor =
+ testSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isMonitorEnter)
+ .findFirst();
+ assertTrue(insertedMonitor.isPresent());
+ assertTrue(testSubject.getLineNumberForInstruction(insertedMonitor.get()) > 0);
+ });
+ }
+
+ public static class Main {
+
+ @NeverInline
+ public static synchronized void test() {
+ synchronized (Main.class) {
+ System.out.println("Hello World!");
+ }
+ }
+
+ public static void main(String[] args) {
+ test();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
index e5635d0..7387f3a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
@@ -33,7 +33,7 @@
@RunWith(Parameterized.class)
public class InlineCatchHandlerWithLibraryTypeTest extends TestBase {
- private static final String TEMPLATE_CODE_EXCEPTION_BINARY_NAME = "java/lang/RuntimeException";
+ private static final String TEMPLATE_CODE_EXCEPTION_BINARY_NAME = "java/lang/Exception";
// A subset of exception types introduced in API levels between 16 to 24.
private static final Map<String, Integer> EXCEPTIONS =
@@ -156,7 +156,7 @@
public static void methodWithCatch() {
try {
maybeThrow();
- } catch (RuntimeException e) {
+ } catch (Exception e) {
// We must use the exception, otherwise there is no move-exception that triggers the
// verification error.
System.out.println(e.getClass().getName());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 84b6917..6037e95 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.optimize.inliner;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -12,6 +16,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.Streams;
import org.junit.Test;
@@ -65,8 +70,18 @@
ExistingException.class)
.addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
.addDontWarn(NonExistingException.class)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + ClassWithCatchNonExisting.class.getTypeName()
+ + ".methodWithCatch()`")))))
.run(parameters.getRuntime(), TestClassCallingMethodWithNonExisting.class)
.assertSuccess();
ClassSubject classSubject =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
index 0d6c727..59aca75 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
@@ -35,40 +34,56 @@
R8
}
+ public enum Rule {
+ EMPTY(""),
+ ASSUME_NO_SIDE_EFFECTS_24(
+ StringUtils.lines(
+ "-assumenosideeffects class android.os.Build$VERSION {",
+ " public static int SDK_INT return 24;",
+ "}")),
+ ASSUME_NO_SIDE_EFFECTS_24_29(
+ StringUtils.lines(
+ "-assumenosideeffects class android.os.Build$VERSION {",
+ " public static int SDK_INT return 24..25;",
+ "}")),
+ ASSUME_VALUES_24(
+ StringUtils.lines(
+ "-assumevalues class android.os.Build$VERSION {",
+ " public static int SDK_INT return 24;",
+ "}")),
+ ASSUME_VALUES_24_29(
+ StringUtils.lines(
+ "-assumevalues class android.os.Build$VERSION {",
+ " public static int SDK_INT return 24..29;",
+ "}"));
+
+ private final String rule;
+
+ Rule(String rule) {
+ this.rule = rule;
+ }
+
+ String getRule() {
+ return rule;
+ }
+ }
+
@Parameter(0)
public TestParameters parameters;
@Parameter(1)
- public String rule;
+ public Rule rule;
@Parameterized.Parameters(name = "{0}, rule: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(),
- ImmutableList.of(
- "",
- StringUtils.lines(
- "-assumenosideeffects class android.os.Build$VERSION {",
- " public static int SDK_INT return 24;",
- "}"),
- StringUtils.lines(
- "-assumenosideeffects class android.os.Build$VERSION {",
- " public static int SDK_INT return 24..25;",
- "}"),
- StringUtils.lines(
- "-assumevalues class android.os.Build$VERSION {",
- " public static int SDK_INT return 24;",
- "}"),
- StringUtils.lines(
- "-assumevalues class android.os.Build$VERSION {",
- " public static int SDK_INT return 24..29;",
- "}")));
+ getTestParameters().withAllRuntimesAndApiLevels().build(), Rule.values());
}
@Test
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
- assumeTrue(rule.equals(""));
+ assumeTrue(rule.getRule().equals(""));
testForD8()
.addProgramClassFileData(getTransformedMainClass())
.addLibraryClassFileData(getTransformedBuildVERSIONClass())
@@ -86,7 +101,7 @@
testForR8(parameters.getBackend())
.addProgramClassFileData(getTransformedMainClass())
.addKeepMainRule(Main.class)
- .addKeepRules(rule)
+ .addKeepRules(rule.getRule())
.addLibraryClassFileData(getTransformedBuildVERSIONClass())
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.setMinApi(parameters.getApiLevel())
@@ -101,7 +116,7 @@
if (compiler == Compiler.D8) {
return parameters.getApiLevel().isLessThan(AndroidApiLevel.N) ? "<N" : ">=N";
} else {
- if (rule.equals("")) {
+ if (rule.getRule().equals("")) {
if (parameters.isDexRuntime()) {
return getExpectedOutput(Compiler.D8);
}
@@ -137,7 +152,7 @@
mainSubject
.streamInstructions()
.anyMatch(x -> x.isStaticGet() && x.getField().getName().toString().equals("SDK_INT"));
- if (compiler == Compiler.D8 || rule.equals("")) {
+ if (compiler == Compiler.D8 || rule.getRule().equals("")) {
assertEquals(
parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
hasIf);
@@ -146,7 +161,7 @@
readsMinSdkField);
} else {
assertFalse(hasIf);
- assertEquals(rule.startsWith("-assumevalues"), readsMinSdkField);
+ assertEquals(rule.getRule().startsWith("-assumevalues"), readsMinSdkField);
}
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 73f7a6f..58d3a74 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -5,7 +5,6 @@
import static org.junit.Assert.fail;
-import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
@@ -26,7 +25,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
public class JasminTestBase extends TestBase {
@@ -107,21 +105,6 @@
return ToolHelper.runR8(builder.build(), optionsConsumer);
}
- protected AndroidApp compileWithR8InDebugMode(
- JasminBuilder builder,
- List<String> proguardConfigs,
- Consumer<InternalOptions> optionsConsumer,
- Backend backend)
- throws Exception {
- R8Command command =
- ToolHelper.prepareR8CommandBuilder(builder.build(), emptyConsumer(backend))
- .addLibraryFiles(runtimeJar(backend))
- .addProguardConfiguration(proguardConfigs, Origin.unknown())
- .setMode(CompilationMode.DEBUG)
- .build();
- return ToolHelper.runR8(command, optionsConsumer);
- }
-
protected AndroidApp compileWithR8(
JasminBuilder builder,
List<String> proguardConfigs,
@@ -283,8 +266,7 @@
}
protected MethodSubject getMethodSubject(
- AndroidApp application, String clazz, MethodSignature signature)
- throws ExecutionException, IOException {
+ AndroidApp application, String clazz, MethodSignature signature) throws IOException {
return new CodeInspector(application).clazz(clazz).method(signature);
}
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
index f215af3..b4e4e71 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
@@ -44,7 +44,8 @@
.setDisableTreeShaking(true)
.setDisableMinification(true)
.addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown())
- .build());
+ .build(),
+ options -> options.getCfCodeAnalysisOptions().setAllowUnreachableCfBlocks(true));
ProcessResult processResult = ToolHelper.runJava(outputJar, main);
assertEquals(0, processResult.exitCode);
return processResult.stdout;
@@ -822,7 +823,10 @@
//
// https://github.com/apache/log4j/blob/v1_2-branch/src/main/java/org/apache/log4j/net/SocketAppender.java#L373
//
- clazz.addVirtualMethod("run", ImmutableList.of(), "V",
+ clazz.addVirtualMethod(
+ "run",
+ ImmutableList.of(),
+ "V",
".limit stack 4",
".limit locals 4",
".var 0 is this Lorg/apache/log4j/net/SocketAppender$Connector; from L0 to L26",
@@ -835,7 +839,8 @@
".line 368",
".line 369",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.reconnectionDelay I",
" i2l",
" invokestatic java/lang/Thread.sleep(J)V",
@@ -846,7 +851,8 @@
" ldc \"Attempting connection to \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
@@ -857,10 +863,12 @@
" new java/net/Socket",
" dup",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.port I",
" invokenonvirtual java/net/Socket.<init>(Ljava/net/InetAddress;I)V",
" astore 1",
@@ -873,7 +881,8 @@
"L6:",
".line 373",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" new java/io/ObjectOutputStream",
" dup",
" aload 1",
@@ -883,9 +892,11 @@
"L7:",
".line 374",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" aconst_null",
- " invokestatic org/apache/log4j/net/SocketAppender.access$1(Lorg/apache/log4j/net/SocketAppender;Lorg/apache/log4j/net/SocketAppender$Connector;)V",
+ " invokestatic"
+ + " org/apache/log4j/net/SocketAppender.access$1(Lorg/apache/log4j/net/SocketAppender;Lorg/apache/log4j/net/SocketAppender$Connector;)V",
"L8:",
".line 375",
" ldc \"Connection established. Exiting connector thread.\"",
@@ -924,7 +935,8 @@
" ldc \"Remote host \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
@@ -949,7 +961,8 @@
" ldc \"Could not connect to \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
- " getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+ " getfield org/apache/log4j/net/SocketAppender$Connector.this$0"
+ + " Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
@@ -978,8 +991,7 @@
".catch all from L6 to L12 using L12",
".catch java/lang/InterruptedException from L2 to L13 using L13",
".catch java/net/ConnectException from L2 to L13 using L16",
- ".catch java/io/IOException from L2 to L13 using L21"
- );
+ ".catch java/io/IOException from L2 to L13 using L21");
// Check that the code compiles without an infinite loop. It cannot run by itself.
AndroidApp app = compileWithD8(builder);
@@ -1065,11 +1077,15 @@
// This is the code for the method
//
- // void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
+ // void org.eclipse.jdt.internal.core.JavaModelOperation.run(
+ // org.eclipse.core.runtime.IProgressMonitor)
//
// from struts2/lib/core-3.1.1.jar
//
- clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"), "V",
+ clazz.addVirtualMethod(
+ "run",
+ ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"),
+ "V",
".limit stack 3",
".limit locals 15",
".var 0 is this Lorg/eclipse/jdt/internal/core/JavaModelOperation; from L0 to L50",
@@ -1085,29 +1101,34 @@
".var 12 is openable Lorg/eclipse/jdt/internal/core/Openable; from L33 to L37",
"L0:",
".line 705",
- " invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+ " invokestatic"
+ + " org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
" astore 2",
"L1:",
".line 706",
" aload 2",
- " invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
" astore 3",
"L2:",
".line 707",
" aload 3",
- " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas"
+ + " Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" istore 4",
"L3:",
".line 709",
" aload 0",
" aload 1",
- " putfield org/eclipse/jdt/internal/core/JavaModelOperation.progressMonitor Lorg/eclipse/core/runtime/IProgressMonitor;",
+ " putfield org/eclipse/jdt/internal/core/JavaModelOperation.progressMonitor"
+ + " Lorg/eclipse/core/runtime/IProgressMonitor;",
"L4:",
".line 710",
" aload 0",
" aload 0",
- " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.pushOperation(Lorg/eclipse/jdt/internal/core/JavaModelOperation;)V",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/JavaModelOperation.pushOperation(Lorg/eclipse/jdt/internal/core/JavaModelOperation;)V",
"L5:",
".line 712",
" aload 0",
@@ -1115,8 +1136,10 @@
" ifeq L6",
"L7:",
".line 715",
- " invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
- " getfield org/eclipse/jdt/internal/core/JavaModelManager.deltaState Lorg/eclipse/jdt/internal/core/DeltaProcessingState;",
+ " invokestatic"
+ + " org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+ " getfield org/eclipse/jdt/internal/core/JavaModelManager.deltaState"
+ + " Lorg/eclipse/jdt/internal/core/DeltaProcessingState;",
" invokevirtual org/eclipse/jdt/internal/core/DeltaProcessingState.initializeRoots()V",
"L6:",
".line 718",
@@ -1164,7 +1187,8 @@
"L20:",
".line 727",
" aload 2",
- " invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
" astore 3",
"L21:",
".line 730",
@@ -1172,7 +1196,8 @@
" istore 9",
"L22:",
" aload 3",
- " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas"
+ + " Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" istore 10",
"L23:",
@@ -1181,11 +1206,13 @@
".line 731",
" aload 3",
" aload 3",
- " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas"
+ + " Ljava/util/ArrayList;",
" iload 9",
" invokevirtual java/util/ArrayList.get(I)Ljava/lang/Object;",
" checkcast org/eclipse/jdt/core/IJavaElementDelta",
- " invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.updateJavaModel(Lorg/eclipse/jdt/core/IJavaElementDelta;)V",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/DeltaProcessor.updateJavaModel(Lorg/eclipse/jdt/core/IJavaElementDelta;)V",
"L26:",
".line 730",
" iinc 9 1",
@@ -1199,7 +1226,8 @@
" istore 9",
"L28:",
" aload 0",
- " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+ " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements"
+ + " [Lorg/eclipse/jdt/core/IJavaElement;",
" arraylength",
" istore 10",
"L29:",
@@ -1207,14 +1235,16 @@
"L31:",
".line 738",
" aload 0",
- " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+ " getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements"
+ + " [Lorg/eclipse/jdt/core/IJavaElement;",
" iload 9",
" aaload",
" astore 11",
"L32:",
".line 739",
" aload 11",
- " invokeinterface org/eclipse/jdt/core/IJavaElement.getOpenable()Lorg/eclipse/jdt/core/IOpenable; 0",
+ " invokeinterface"
+ + " org/eclipse/jdt/core/IJavaElement.getOpenable()Lorg/eclipse/jdt/core/IOpenable; 0",
" checkcast org/eclipse/jdt/internal/core/Openable",
" astore 12",
"L33:",
@@ -1229,7 +1259,8 @@
"L34:",
".line 741",
" aload 12",
- " invokevirtual org/eclipse/jdt/internal/core/Openable.getParent()Lorg/eclipse/jdt/core/IJavaElement;",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/Openable.getParent()Lorg/eclipse/jdt/core/IJavaElement;",
" checkcast org/eclipse/jdt/internal/core/JavaElement",
" invokevirtual org/eclipse/jdt/internal/core/JavaElement.close()V",
"L35:",
@@ -1243,7 +1274,9 @@
"L36:",
".line 746",
" aload 11",
- " invokeinterface org/eclipse/jdt/core/IJavaElement.getJavaProject()Lorg/eclipse/jdt/core/IJavaProject; 0",
+ " invokeinterface"
+ + " org/eclipse/jdt/core/IJavaElement.getJavaProject()Lorg/eclipse/jdt/core/IJavaProject;"
+ + " 0",
" checkcast org/eclipse/jdt/internal/core/JavaProject",
" invokevirtual org/eclipse/jdt/internal/core/JavaProject.resetCaches()V",
"L37:",
@@ -1261,12 +1294,14 @@
"L40:",
".line 756",
" aload 3",
- " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas"
+ + " Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" iload 4",
" if_icmpgt L41",
" aload 3",
- " getfield org/eclipse/jdt/internal/core/DeltaProcessor.reconcileDeltas Ljava/util/HashMap;",
+ " getfield org/eclipse/jdt/internal/core/DeltaProcessor.reconcileDeltas"
+ + " Ljava/util/HashMap;",
" invokevirtual java/util/HashMap.isEmpty()Z",
" ifne L39",
"L41:",
@@ -1279,7 +1314,8 @@
" aload 3",
" aconst_null",
" iconst_0",
- " invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.fire(Lorg/eclipse/jdt/core/IJavaElementDelta;I)V",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/DeltaProcessor.fire(Lorg/eclipse/jdt/core/IJavaElementDelta;I)V",
" goto L39",
"L43:",
".line 761",
@@ -1295,7 +1331,8 @@
"L46:",
".line 762",
" aload 0",
- " invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.popOperation()Lorg/eclipse/jdt/internal/core/JavaModelOperation;",
+ " invokevirtual"
+ + " org/eclipse/jdt/internal/core/JavaModelOperation.popOperation()Lorg/eclipse/jdt/internal/core/JavaModelOperation;",
" pop",
"L47:",
".line 763",
@@ -1316,8 +1353,7 @@
".catch all from L3 to L17 using L17",
".catch all from L16 to L49 using L17",
".catch all from L20 to L43 using L43",
- ".catch all from L39 to L48 using L43"
- );
+ ".catch all from L39 to L48 using L43");
// Check that the code compiles without an infinite loop. It cannot run by itself.
AndroidApp app = compileWithD8(builder);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
index 8230cef..ee16233 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -47,7 +46,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepKotlinMetadata()
.addKeepRules(StringUtils.joinLines("-if class *.Metadata", "-keep class <1>.io.** { *; }"))
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
index 1acdcf2..7a7aec4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -49,7 +48,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRuleWithAllowObfuscation()
.addKeepKotlinMetadata()
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index 8bcbc9e..1c93e08 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -90,7 +90,6 @@
.addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
.addKeepRules("-keep class **.Anno")
.addKeepKotlinMetadata()
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
.assertWarningMessageThatMatches(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index 68731b5..16fc35d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -42,7 +42,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepKotlinMetadata()
.addKeepRules("-keep class kotlin.io.** { *; }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index 64e7bd8..ceb7d56 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -50,7 +50,6 @@
.addKeepAllClassesRule()
.addKeepKotlinMetadata()
.addKeepAttributes(
- ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
.allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
index 07e22c5..5d13395 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
@@ -101,7 +101,6 @@
kotlinc.getKotlinStdlibJar())
.addKeepClassAndMembersRules(PKG_LIB + ".*")
.addKeepAttributes(
- ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 5d715f1..baf09cc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -55,7 +54,6 @@
.addProgramFiles(kotlinc.getKotlinReflectJar(), kotlinc.getKotlinAnnotationJar())
.addKeepMainRule(mainClassName)
.addKeepKotlinMetadata()
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index 4e56011..d94ad7b 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -56,17 +56,20 @@
@Test
public void roundTripTest() throws IOException {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
- ClassNameMapper secondMapper = ClassNameMapper.mapperFromString(firstMapper.toString());
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
+ ClassNameMapper secondMapper =
+ ClassNameMapper.mapperFromString(firstMapper.toString()).sorted();
Assert.assertEquals(firstMapper, secondMapper);
}
@Test
public void roundTripTestWithLeadingBOM() throws IOException {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
ClassNameMapper secondMapper =
- ClassNameMapper.mapperFromString(StringUtils.BOM + firstMapper.toString());
+ ClassNameMapper.mapperFromString(StringUtils.BOM + firstMapper.toString()).sorted();
assertTrue(secondMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, secondMapper);
byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
@@ -76,7 +79,7 @@
assertEquals(0xef, Byte.toUnsignedLong(bytes[0]));
assertEquals(0xbb, Byte.toUnsignedLong(bytes[1]));
assertEquals(0xbf, Byte.toUnsignedLong(bytes[2]));
- ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM);
+ ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM).sorted();
assertTrue(thirdMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, thirdMapper);
}
@@ -93,7 +96,8 @@
"" + StringUtils.BOM,
StringUtils.BOM + " " + StringUtils.BOM);
for (String whitespace : ws) {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
StringBuilder buildWithWhitespace = new StringBuilder();
char prevChar = '\0';
@@ -114,13 +118,13 @@
prevChar = c;
}
ClassNameMapper secondMapper =
- ClassNameMapper.mapperFromString(buildWithWhitespace.toString());
+ ClassNameMapper.mapperFromString(buildWithWhitespace.toString()).sorted();
assertFalse(firstMapper.toString().contains("" + StringUtils.BOM));
Assert.assertEquals(firstMapper, secondMapper);
byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
assertNotEquals(0xef, Byte.toUnsignedLong(bytes[0]));
Path mapFileWithBOM = writeTextToTempFile(StringUtils.BOM + firstMapper.toString());
- ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM);
+ ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM).sorted();
assertTrue(thirdMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, thirdMapper);
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 4fdc4b7..1619359 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -12,8 +12,9 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
import com.android.tools.r8.retrace.RetraceCommand;
-import com.android.tools.r8.retrace.RetraceHelper;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Equivalence;
@@ -378,13 +379,16 @@
List<String> stackTrace =
stackTraceLines.stream().map(line -> line.originalLine).collect(Collectors.toList());
stackTrace.add(0, exceptionLine);
- RetraceHelper.runForTesting(
+ Retrace.run(
RetraceCommand.builder()
- .setProguardMapProducer(ProguardMapProducer.fromString(map))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromString(map))
+ .setAllowExperimental(allowExperimentalMapping)
+ .build())
.setStackTrace(stackTrace)
.setRetracedStackTraceConsumer(box::set)
- .build(),
- allowExperimentalMapping);
+ .build());
// Keep the original stderr in the retraced stacktrace.
return new StackTrace(
box.get().get(0), internalConvert(box.get().stream().skip(1)), originalStderr);
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
index 34da9a6..4372134 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.RetraceOptions;
-import com.android.tools.r8.retrace.StringRetrace;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -98,13 +100,18 @@
@Test
public void testCatchAllRangeR8() {
- List<String> retrace =
- StringRetrace.create(
- RetraceOptions.builder()
+ Box<String> retracedString = new Box<>();
+ Retrace.run(
+ RetraceCommand.builder()
+ .setRetracedStackTraceConsumer(
+ retraced -> retracedString.set(StringUtils.lines(retraced)))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
.build())
- .retrace(Arrays.asList(stackTrace));
- assertEquals(retracedR8, StringUtils.lines(retrace));
+ .setStackTrace(Arrays.asList(stackTrace))
+ .build());
+ assertEquals(retracedR8, retracedString.get());
}
private String getExpected() {
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationEnumParameterTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationEnumParameterTest.java
new file mode 100644
index 0000000..53912a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationEnumParameterTest.java
@@ -0,0 +1,80 @@
+// 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.proto;
+
+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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a regression test for b/235733922 */
+@RunWith(Parameterized.class)
+public class ProtoNormalizationEnumParameterTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepClassRules(CustomAnnotation.class)
+ .addKeepRuntimeVisibleParameterAnnotations()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class, "foo", "bar")
+ .assertSuccessWithOutputLines("2foobar", "TEST_1");
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.PARAMETER)
+ public @interface CustomAnnotation {}
+
+ public enum MyEnum {
+ TEST_1("Foo"),
+ TEST_2("Bar");
+
+ private final String str;
+
+ MyEnum(@CustomAnnotation String str) {
+ this.str = str;
+ }
+
+ public String getStr() {
+ return TEST_1 + str;
+ }
+ }
+
+ public static class Main {
+
+ @NeverInline
+ // The test(int foo, String bar, String baz) is needed to have
+ // MyEnum.<init>(String name, int ordinal, String str) written into this proto.
+ public static void test(int foo, String bar, String baz) {
+ System.out.println(foo + bar + baz);
+ }
+
+ @NeverInline
+ public static void main(String[] args) {
+ test(args.length, args[0], args[1]);
+ MyEnum val = System.currentTimeMillis() > 0 ? MyEnum.TEST_1 : MyEnum.TEST_2;
+ System.out.println(val);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress181837660.java b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
index b5e0a7c..d1275c8 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress181837660.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dexsplitter.SplitterTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -88,13 +89,17 @@
assertEquals(processResult.stdout, "42\n");
}
- private void configure(R8FullTestBuilder testBuilder) throws NoSuchMethodException {
- testBuilder.enableInliningAnnotations().noMinification().setMinApi(parameters.getApiLevel());
+ private void configure(R8FullTestBuilder testBuilder) {
+ configureNoInlineAnnotations(testBuilder);
+ testBuilder.enableInliningAnnotations();
}
- private void configureNoInlineAnnotations(R8FullTestBuilder testBuilder)
- throws NoSuchMethodException {
- testBuilder.noMinification().setMinApi(parameters.getApiLevel());
+ private void configureNoInlineAnnotations(R8FullTestBuilder testBuilder) {
+ testBuilder
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
+ .noMinification()
+ .setMinApi(parameters.getApiLevel());
}
public static class BaseClass {
diff --git a/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java b/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java
index b27e96a..56fb81a 100644
--- a/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java
+++ b/src/test/java/com/android/tools/r8/regress/UndefinedLambdaInterfaceRegress232379893.java
@@ -3,10 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
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 com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,7 +41,21 @@
.addKeepMainRule(TestClass.class)
.addDontWarn(UndefinedInterface.class)
.addDontShrink()
+ .allowDiagnosticWarningMessages(parameters.isDexRuntime())
.setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (parameters.isDexRuntime()) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + TestClass.class.getTypeName()
+ + ".main(java.lang.String[])`"))));
+ }
+ })
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index a2ca851..077e3c2 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress.b78493232;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.AsmTestBase;
@@ -17,6 +19,7 @@
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;
// Variant of Regress78493232, but where the new-instance is forced to flow to a non-trivial phi
@@ -35,17 +38,14 @@
private static final List<byte[]> CLASS_BYTES =
ImmutableList.of(Regress78493232Dump_WithPhi.dump());
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public Regress78493232_WithPhi(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void testReference() throws Exception {
assumeTrue(parameters.isCfRuntime());
@@ -86,11 +86,22 @@
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.addProgramClassFileData(CLASS_BYTES)
+ .allowDiagnosticWarningMessages()
.treeShaking(treeShake)
.noMinification()
.setMinApi(parameters.getApiLevel())
.addOptionsModification(options -> options.testing.readInputStackMaps = false)
.addKeepMainRule(MAIN)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(
+ equalTo(
+ "Unverifiable code in `java.lang.String regress78493232.Test."
+ + "methodCausingIssue(byte, short, int)` at instruction 53: "
+ + "Cannot join stacks, expected frame types at stack index 1 "
+ + "to join to a precise (non-top) type, but types null and "
+ + "uninitialized java.lang.String do not."))))
.run(parameters.getRuntime(), MAIN);
checkResult(result);
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
index 2265ba0..16a1089 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageLambdaMissingInterfaceTest.java
@@ -4,13 +4,18 @@
package com.android.tools.r8.repackage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,9 +51,22 @@
.applyIf(repackage, this::configureRepackaging)
.setMinApi(parameters.getApiLevel())
.addDontWarn(MissingInterface.class)
+ .allowDiagnosticWarningMessages(parameters.isDexRuntime())
.noClassInlining()
.enableInliningAnnotations()
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (parameters.isDexRuntime()) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + ClassWithLambda.class.getTypeName()
+ + ".callWithLambda()`"))));
+ }
+ })
.inspect(
inspector -> {
// Find the generated lambda class
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
index 87fe5f0..2255c28 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -42,6 +43,8 @@
public void test() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(BaseClass.class)
+ // Link against android.jar that contains ReflectiveOperationException.
+ .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
.addFeatureSplit(FeatureMain.class, FeatureClass.class)
.addFeatureSplitRuntime()
.addKeepFeatureMainRule(FeatureMain.class)
diff --git a/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
index 06899fd..5dcca78 100644
--- a/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/DuplicateMappingsTest.java
@@ -37,13 +37,16 @@
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
Retrace.run(
RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(
- ProguardMapProducer.fromString(
- StringUtils.lines(
- "com.android.tools.r8.retrace.SourceFileTest$ClassWithCustomFileName ->"
- + " com.android.tools.r8.retrace.a:",
- "# {'id':'sourceFile','fileName':'foobarbaz.java'}",
- "# {'id':'sourceFile','fileName':'foobarbaz2.java'}")))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromString(
+ StringUtils.lines(
+ "com.android.tools.r8.retrace.SourceFileTest"
+ + "$ClassWithCustomFileName -> com.android.tools.r8.retrace.a:",
+ "# {'id':'sourceFile','fileName':'foobarbaz.java'}",
+ "# {'id':'sourceFile','fileName':'foobarbaz2.java'}")))
+ .build())
.setStackTrace(ImmutableList.of(" at com.android.tools.r8.retrace.a.foo(Unknown)"))
.setRetracedStackTraceConsumer(
strings -> {
diff --git a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
index 56622ac..3aa2f49 100644
--- a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
@@ -55,11 +55,14 @@
.collect(Collectors.toList());
RetraceCommand.Builder builder =
RetraceCommand.builder()
- .setProguardMapProducer(ProguardMapProducer.fromString(run.proguardMap()))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromString(run.proguardMap()))
+ .build())
.setStackTrace(originalStackTrace)
.setRetracedStackTraceConsumer(box::set)
.setVerbose(true);
- RetraceHelper.runForTesting(builder.build(), false);
+ Retrace.run(builder.build());
// TODO(b/221015863): This should ideally be:
// at " + typeName(ClassWithOverload.class) + ".test(int)(OverloadsWithoutLineNumberTest.java)
if (parameters.canUseNativeDexPC()) {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 31dd142..4602712 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -85,6 +85,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
@@ -95,20 +96,26 @@
@RunWith(Parameterized.class)
public class RetraceTests extends TestBase {
- @Parameters(name = "{0}, external: {1}, verbose: {2}")
+ @Parameters(name = "{0}, external: {1}, verbose: {2}, stream: {3}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withCfRuntimes().build(), BooleanUtils.values(), BooleanUtils.values());
+ getTestParameters().withCfRuntimes().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
}
private final TestParameters testParameters;
private final boolean external;
private final boolean verbose;
+ private final boolean stream;
- public RetraceTests(TestParameters parameters, boolean external, boolean verbose) {
+ public RetraceTests(
+ TestParameters parameters, boolean external, boolean verbose, boolean stream) {
this.testParameters = parameters;
this.external = external;
this.verbose = verbose;
+ this.stream = stream;
}
@Test
@@ -152,13 +159,16 @@
NullStackTrace nullStackTrace = new NullStackTrace();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(ProguardMapProducer.fromString(nullStackTrace.mapping()))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromString(nullStackTrace.mapping()))
+ .build())
.setStackTrace(nullStackTrace.obfuscatedStackTrace())
.setRetracedStackTraceConsumer(retraced -> fail())
.build();
try {
Retrace.run(retraceCommand);
- fail();
} catch (RetraceAbortException e) {
diagnosticsHandler.assertOnlyErrors();
diagnosticsHandler.assertErrorsCount(1);
@@ -491,16 +501,38 @@
return new TestDiagnosticMessagesImpl();
} else {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
- RetraceCommand retraceCommand =
+ StringBuilder retracedStackTraceBuilder = new StringBuilder();
+ RetraceCommand.Builder builder =
RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(ProguardMapProducer.fromString(stackTraceForTest.mapping()))
- .setStackTrace(stackTraceForTest.obfuscatedStackTrace())
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .setAllowExperimental(allowExperimentalMapping)
+ .build())
.setRetracedStackTraceConsumer(
- retraced -> assertEquals(expectedStackTrace, StringUtils.joinLines(retraced)))
- .setVerbose(verbose)
- .build();
- Retrace.runForTesting(retraceCommand, allowExperimentalMapping);
+ retraced -> {
+ if (retracedStackTraceBuilder.length() > 0) {
+ retracedStackTraceBuilder.append(StringUtils.LINE_SEPARATOR);
+ }
+ retracedStackTraceBuilder.append(StringUtils.joinLines(retraced));
+ })
+ .setVerbose(verbose);
+ setStacktraceSupplierAndRetraceConsumer(builder, stackTraceForTest.obfuscatedStackTrace());
+ RetraceCommand retraceCommand = builder.build();
+ Retrace.run(retraceCommand);
+ assertEquals(expectedStackTrace, retracedStackTraceBuilder.toString());
return diagnosticsHandler;
}
}
+
+ private void setStacktraceSupplierAndRetraceConsumer(
+ RetraceCommand.Builder builder, List<String> stackTrace) {
+ if (stream) {
+ Iterator<String> iterator = stackTrace.iterator();
+ builder.setStackTrace(() -> iterator.hasNext() ? ImmutableList.of(iterator.next()) : null);
+ } else {
+ builder.setStackTrace(stackTrace);
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java
index 7b9c08d..17b6600 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceWhitespaceTest.java
@@ -64,7 +64,10 @@
() ->
Retrace.run(
RetraceCommand.builder()
- .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+ .build())
.setStackTrace(STACKTRACE)
.setRetracedStackTraceConsumer(lines -> {})
.build()));
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index 91428db..1ea73cc 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -1140,7 +1140,11 @@
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
- .setProguardMapProducer(ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(
+ ProguardMapProducer.fromString(stackTraceForTest.mapping()))
+ .build())
.setStackTrace(stackTraceForTest.obfuscatedStackTrace())
.setRetracedStackTraceConsumer(
retraced -> {
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
index 9ca6e87..8a86283 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiInlineInOutlineTest.java
@@ -9,9 +9,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
@@ -63,12 +62,11 @@
@Test
public void test() {
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .allowLookupAllClasses()
.build();
- Retracer retracer = Retracer.builder().setMappingProvider(mappingProvider).build();
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
index 650953c..cfefe61 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInOutlineStackTrace.java
@@ -10,9 +10,8 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
@@ -71,12 +70,11 @@
@Test
public void test() {
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .allowLookupAllClasses()
.build();
- Retracer retracer = Retracer.builder().setMappingProvider(mappingProvider).build();
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
// Retrace the first outline.
RetraceStackTraceContext outlineContext =
retraceOutline(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
index 1c67ac5..34f1217 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineInlineTest.java
@@ -9,9 +9,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
@@ -62,12 +61,11 @@
@Test
public void test() {
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingSupplier =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .allowLookupAllClasses()
.build();
- Retracer retracer = Retracer.builder().setMappingProvider(mappingProvider).build();
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
index d77819d..38736ce 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiOutlineNoInlineTest.java
@@ -10,9 +10,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetracedMethodReference;
@@ -62,12 +61,11 @@
@Test
public void test() {
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .allowLookupAllClasses()
.build();
- Retracer retracer = Retracer.builder().setMappingProvider(mappingProvider).build();
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
List<RetraceFrameElement> outlineRetraced =
retracer
.retraceFrame(
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
index 6251b54..76b0ff4 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeResidualTest.java
@@ -10,9 +10,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetraceThrownExceptionElement;
@@ -80,12 +79,11 @@
@Test
public void testUsingObfuscatedName() {
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .allowLookupAllClasses()
.build();
- Retracer retracer = Retracer.builder().setMappingProvider(mappingProvider).build();
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingProvider).build();
List<RetraceThrownExceptionElement> npeRetraced =
retracer.retraceThrownException(renamedException).stream().collect(Collectors.toList());
assertEquals(1, npeRetraced.size());
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
index 6d3bd5b..4334060 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiRewriteFrameInlineNpeTest.java
@@ -11,9 +11,8 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.MappingProvider;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.ProguardMappingProvider;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
import com.android.tools.r8.retrace.RetraceFrameElement;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
import com.android.tools.r8.retrace.RetraceThrownExceptionElement;
@@ -60,15 +59,13 @@
@Test
public void testFirstStackLineIsRemoved() {
TestDiagnosticsHandler testDiagnosticsHandler = new TestDiagnosticsHandler();
- MappingProvider mappingProvider =
- ProguardMappingProvider.builder()
+ ProguardMappingSupplier mappingProvider =
+ ProguardMappingSupplier.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(mapping))
- .setDiagnosticsHandler(testDiagnosticsHandler)
- .allowLookupAllClasses()
.build();
Retracer retracer =
Retracer.builder()
- .setMappingProvider(mappingProvider)
+ .setMappingSupplier(mappingProvider)
.setDiagnosticsHandler(testDiagnosticsHandler)
.build();
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index 5e4932e..7d4a46b 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.rewrite;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -79,6 +80,9 @@
containsString("Missing class "),
containsString(
"required for default or static interface methods desugaring"),
+ allOf(
+ containsString("Unverifiable code in `"),
+ containsString("org.mozilla.javascript.tools.")),
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))))
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index 2ddf11b..33394dd 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.rewrite;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -36,21 +37,20 @@
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 ScriptEngineTest extends ScriptEngineTestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public ScriptEngineTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws IOException, CompilationFailedException, ExecutionException {
Path path = temp.newFile("out.zip").toPath();
@@ -96,6 +96,9 @@
containsString("Missing class "),
containsString(
"it is required for default or static interface methods desugaring"),
+ allOf(
+ containsString("Unverifiable code in `"),
+ containsString("org.mozilla.javascript.tools.")),
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))))
.writeToZip(path)
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
index c3e53cf..5fec641 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
@@ -30,22 +30,22 @@
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 FieldReadsJasminTest extends JasminTestBase {
private static final String CLS = "Empty";
private static final String MAIN = "Main";
- private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public FieldReadsJasminTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void testInstanceGet_nonNullReceiver() throws Exception {
JasminBuilder builder = new JasminBuilder();
@@ -284,20 +284,21 @@
" bipush 1",
" putstatic Main/sField I",
" return");
- MethodSignature mainMethod = main.addMainMethod(
- ".limit stack 3",
- ".limit locals 2",
- " getstatic Main/sField I",
- " new Empty",
- " dup",
- " invokespecial Empty/<init>()V",
- " getstatic Main/sField I",
- " bipush 2",
- " if_icmpeq r",
- " aconst_null",
- " athrow",
- "r:",
- " return");
+ MethodSignature mainMethod =
+ main.addMainMethod(
+ ".limit stack 4",
+ ".limit locals 2",
+ " getstatic Main/sField I",
+ " new Empty",
+ " dup",
+ " invokespecial Empty/<init>()V",
+ " getstatic Main/sField I",
+ " bipush 2",
+ " if_icmpeq r",
+ " aconst_null",
+ " athrow",
+ "r:",
+ " return");
ensureFieldExistsAndReadOnlyOnce(builder, main, mainMethod, main, "sField");
}
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 bb63e6f..acc8765 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
@@ -21,6 +23,7 @@
import com.android.tools.r8.jasmin.JasminTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
@@ -311,14 +314,22 @@
})
.allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
.setMinApi(parameters.getApiLevel())
- .compile()
- .applyIf(
- allowDiagnosticWarningMessages,
- result ->
- result.assertAllWarningMessagesMatch(
- equalTo(
- "The method `void UnverifiableClass.unverifiableMethod()` does not"
- + " type check and will be assumed to be unreachable.")))
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (allowDiagnosticWarningMessages) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void"
+ + " UnverifiableClass.unverifiableMethod()`"))),
+ diagnosticMessage(
+ equalTo(
+ "The method `void UnverifiableClass.unverifiableMethod()` does not"
+ + " type check and will be assumed to be unreachable.")));
+ }
+ })
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(r8Result, Compiler.R8);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
index 23febd2..59fcf6d 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideCovariantTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentHashMap.KeySetView;
@@ -61,14 +60,10 @@
.compile()
.addRunClasspathFiles(buildOnDexRuntime(parameters, LibraryUser.class))
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatchesIf(
- parameters.isCfRuntime(), containsString("Hello World"))
- .assertFailureWithErrorThatThrowsIf(
- !supportsKeySetView() && parameters.isDexRuntime(), NoSuchMethodError.class)
- // TODO(b/234579501): We fail to keep the library method override.
.applyIf(
- parameters.isDexRuntime() && supportsKeySetView(),
- TestRunResult::assertSuccessWithEmptyOutput);
+ supportsKeySetView(),
+ result -> result.assertFailureWithErrorThatMatches(containsString("Hello World")),
+ result -> result.assertFailureWithErrorThatThrows(NoSuchMethodError.class));
}
private byte[] getMainWithoutSyntheticBridgeForKeySet() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index 0222e3d..82d690c 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -3,12 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
@@ -40,7 +44,18 @@
.addKeepMainRule(TestClassForB112849320.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
.addKeepPackageNamesRule(GoingToBeMissed.class.getPackage())
- .compile()
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + "void "
+ + TestClassForB112849320.class.getTypeName()
+ + ".main(java.lang.String[])`")))))
.addRunClasspathFiles(
buildOnDexRuntime(
parameters,
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index d241adb..e32e47c 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -66,10 +66,11 @@
.addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
.addKeepRules("-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX))
.applyIf(
- test.equals("shaking12")
- && parameters.isDexRuntime()
- && parameters.getApiLevel().isLessThan(AndroidApiLevel.K),
- builder -> builder.addDontWarn(ReflectiveOperationException.class))
+ test.equals("shaking12") && parameters.isDexRuntime(),
+ testBuilder ->
+ // Link against android.jar that contains ReflectiveOperationException.
+ testBuilder.addLibraryFiles(
+ parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K)))
// Disable inlining to make this test not depend on inlining decisions.
.addOptionsModification(o -> o.inlinerOptions().enableInlining = false)
.enableProguardTestOptions()
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index a841c79..9c3bbea 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -92,6 +92,8 @@
VALID_PROGUARD_DIR + "assume-values-with-return-value.flags";
private static final String ADAPT_KOTLIN_METADATA =
VALID_PROGUARD_DIR + "adapt-kotlin-metadata.flags";
+ private static final String KEEP_KOTLIN_METADATA =
+ VALID_PROGUARD_DIR + "keep-kotlin-metadata.flags";
private static final String INCLUDING =
VALID_PROGUARD_DIR + "including.flags";
private static final String INVALID_INCLUDING_1 =
@@ -897,7 +899,22 @@
new ProguardConfigurationParser(new DexItemFactory(), reporter);
Path path = Paths.get(ADAPT_KOTLIN_METADATA);
parser.parse(path);
- checkDiagnostics(handler.infos, path, 1, 1, "Ignoring", "-adaptkotlinmetadata");
+ verifyParserEndsCleanly();
+ }
+
+ @Test
+ public void parseKeepKotlinMetadata() {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ Path path = Paths.get(KEEP_KOTLIN_METADATA);
+ parser.parse(path);
+ verifyParserEndsCleanly();
+ ProguardConfiguration config = parser.getConfig();
+ assertEquals(
+ "-keepattributes RuntimeVisibleAnnotations", config.getKeepAttributes().toString());
+ assertEquals(
+ StringUtils.joinLines("-keep class kotlin.Metadata {", " *;", "}"),
+ config.getRules().get(0).toString());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index 9b5cc93..ce5e0fe 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -88,12 +88,15 @@
classBuilder = builder.addClass(compatLibraryClassName);
- classBuilder.addStaticMethod("compatMethod", ImmutableList.of(), "V",
+ classBuilder.addStaticMethod(
+ "compatMethod",
+ ImmutableList.of(),
+ "V",
+ ".limit stack 2",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \" Compat\"",
" invokevirtual java/io/PrintStream/print(Ljava.lang.String;)V",
- " return"
- );
+ " return");
classBuilder.addStaticMethod("method", ImmutableList.of(), "V",
".limit stack 2",
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceMethodResolutionWithMissingLibraryAndProgramClassTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceMethodResolutionWithMissingLibraryAndProgramClassTest.java
index f15630e..5fa184a 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceMethodResolutionWithMissingLibraryAndProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceMethodResolutionWithMissingLibraryAndProgramClassTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
@@ -103,7 +104,7 @@
Reference.methodFromMethod(B.class.getMethod("baz")),
Reference.methodFromMethod(B.class.getMethod("qux")));
assertEquals(foundSet, consumer.seenMethods);
- assertEquals(foundSet, consumer.seenMissingMethods);
+ assertEquals(Collections.emptySet(), consumer.seenMissingMethods);
}
// A is added to both library and program, but the program one is missing the methods {foo,bar}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index efcf8a9..74b5de5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -36,7 +36,7 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.Retracer;
-import com.android.tools.r8.retrace.internal.ProguardMappingProviderImpl;
+import com.android.tools.r8.retrace.internal.ProguardMappingSupplierImpl;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
@@ -185,7 +185,7 @@
if (lazyRetracer == null) {
lazyRetracer =
Retracer.builder()
- .setMappingProvider(new ProguardMappingProviderImpl(mapping))
+ .setMappingSupplier(new ProguardMappingSupplierImpl(mapping))
.setDiagnosticsHandler(new TestDiagnosticMessagesImpl())
.build();
}
@@ -549,8 +549,8 @@
public Retracer retrace() {
return Retracer.builder()
- .setMappingProvider(
- new ProguardMappingProviderImpl(
+ .setMappingSupplier(
+ new ProguardMappingSupplierImpl(
mapping == null ? ClassNameMapper.builder().build() : mapping))
.setDiagnosticsHandler(new TestDiagnosticMessagesImpl())
.build();
diff --git a/src/test/java/com/android/tools/r8/workaround/InputWithAbstractMethodOnNonAbstractClassTest.java b/src/test/java/com/android/tools/r8/workaround/InputWithAbstractMethodOnNonAbstractClassTest.java
index aca08d4..f44fff9 100644
--- a/src/test/java/com/android/tools/r8/workaround/InputWithAbstractMethodOnNonAbstractClassTest.java
+++ b/src/test/java/com/android/tools/r8/workaround/InputWithAbstractMethodOnNonAbstractClassTest.java
@@ -74,7 +74,7 @@
private void inspect(CodeInspector inspector) {
MethodSubject methodOfInterest = inspector.clazz(Greeter.class).uniqueMethodWithName("dead");
assertThat(methodOfInterest, isPresent());
- if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
+ if (parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
assertThat(methodOfInterest, not(isAbstract()));
} else {
assertThat(methodOfInterest, isAbstract());
diff --git a/src/test/proguard/valid/keep-kotlin-metadata.flags b/src/test/proguard/valid/keep-kotlin-metadata.flags
new file mode 100644
index 0000000..b5a6fcd
--- /dev/null
+++ b/src/test/proguard/valid/keep-kotlin-metadata.flags
@@ -0,0 +1 @@
+-keepkotlinmetadata
\ No newline at end of file
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index ee68444..3a106aa 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-72bdd118be45bbd99762b5dfea457c90052e0f22
\ No newline at end of file
+da5a2b797563b19461f62db48bf1e45e2f86752f
\ No newline at end of file
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 30ac55d..abfaa5e 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-c7b9bd1e5be6caa0e8a8cb6634b7fb73fd48ae8e
\ No newline at end of file
+29e9654bef01ef4e00b0e44849b9073a02997b8b
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 9f3d1b0..5962cc0 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -129,6 +129,16 @@
help='Run the compilation in a loop',
default=False,
action='store_true')
+ parser.add_argument(
+ '--enable-missing-library-api-modeling',
+ help='Run with api modeling',
+ default=False,
+ action='store_true')
+ parser.add_argument(
+ '--android-platform-build',
+ help='Run as a platform build',
+ default=False,
+ action='store_true')
return parser
def error(msg):
@@ -271,6 +281,20 @@
return True
return None
+def determine_android_platform_build(args, build_properties):
+ if args.android_platform_build:
+ return args.android_platform_build
+ if 'android-platform-build=true' in build_properties:
+ return True
+ return None
+
+def determine_enable_missing_library_api_modeling(args, build_properties):
+ if args.enable_missing_library_api_modeling:
+ return args.enable_missing_library_api_modeling
+ if 'enable-missing-library-api-modeling=true' in build_properties:
+ return True
+ return None
+
def determine_properties(build_properties):
args = []
for key, value in build_properties.items():
@@ -367,6 +391,8 @@
out = determine_output(args, temp)
min_api = determine_min_api(args, build_properties)
classfile = determine_class_file(args, build_properties)
+ android_platform_build = determine_android_platform_build(args, build_properties)
+ enable_missing_library_api_modeling = determine_enable_missing_library_api_modeling(args, build_properties)
jar = args.r8_jar if args.r8_jar else download_distribution(version, args.nolib, temp)
if ':' not in jar and not os.path.exists(jar):
error("Distribution does not exist: " + jar)
@@ -429,6 +455,10 @@
cmd.extend(['--min-api', min_api])
if classfile:
cmd.extend(['--classfile'])
+ if android_platform_build:
+ cmd.extend(['--android-platform-build'])
+ if enable_missing_library_api_modeling:
+ cmd.extend(['--enable-missing-library-api-modeling'])
if args.threads:
cmd.extend(['--threads', args.threads])
cmd.extend(otherargs)