Merge commit 'a89dbbd97c51b46d21f7cccaca7e45dcb1ecc792' into dev-release
diff --git a/.gitignore b/.gitignore
index b3f67f4..e1b9f6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -207,6 +207,8 @@
third_party/opensource-apps/sqldelight.tar.gz
third_party/opensource-apps/sunflower
third_party/opensource-apps/sunflower.tar.gz
+third_party/opensource-apps/systemui
+third_party/opensource-apps/systemui.tar.gz
third_party/opensource-apps/tachiyomi
third_party/opensource-apps/tachiyomi.tar.gz
third_party/opensource-apps/tivi
diff --git a/build.gradle b/build.gradle
index b2e669c..7c3a95c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,6 +4,7 @@
import dx.DexMergerTask
import dx.DxTask
+import desugaredlibrary.CustomConversionAsmRewriterTask
import net.ltgt.gradle.errorprone.CheckSeverity
import org.gradle.internal.os.OperatingSystem
import smali.SmaliTask
@@ -31,7 +32,7 @@
ext {
androidSupportVersion = '25.4.0'
- asmVersion = '9.3' // When updating update tools/asmifier.py and Toolhelper as well.
+ asmVersion = '9.3' // When updating update tools/asmifier.py, build.src and Toolhelper as well.
espressoVersion = '3.0.0'
fastutilVersion = '7.2.0'
guavaVersion = '30.1.1-jre'
@@ -1007,11 +1008,16 @@
}
}
-task buildLibraryDesugarConversions(type: Zip, dependsOn: downloadDeps) {
+task rawBuildLibraryDesugarConversions(type: Zip, dependsOn: downloadDeps) {
from sourceSets.libraryDesugarConversions.output
include "java/**/*.class"
- baseName 'library_desugar_conversions'
- destinationDir file('build/libs')
+ baseName 'library_desugar_conversions_raw'
+ destinationDir file('build/tmp/desugaredlibrary')
+}
+
+task buildLibraryDesugarConversions(type: CustomConversionAsmRewriterTask, dependsOn: rawBuildLibraryDesugarConversions) {
+ rawJar = file("build/tmp/desugaredlibrary/library_desugar_conversions_raw.zip")
+ outputDirectory = file("build/libs")
}
task testJarSources(type: Jar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 74c5cfd..81527b4 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -7,10 +7,18 @@
repositories {
mavenCentral()
}
+ext {
+ asmVersion = '9.3'
+}
dependencies {
compile group: 'com.google.guava', name: 'guava', version: '19.0'
compile group: 'org.smali', name: 'smali', version: '2.2b4'
+ compile group: 'org.ow2.asm', name: 'asm', version: asmVersion
+ compile group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
+ compile group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
+ compile group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
+ compile group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
}
sourceCompatibility = JavaVersion.VERSION_1_8
diff --git a/buildSrc/src/main/java/desugaredlibrary/AsmRewriter.java b/buildSrc/src/main/java/desugaredlibrary/AsmRewriter.java
new file mode 100644
index 0000000..cc1fcf2
--- /dev/null
+++ b/buildSrc/src/main/java/desugaredlibrary/AsmRewriter.java
@@ -0,0 +1,53 @@
+// 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 desugaredlibrary;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public abstract class AsmRewriter {
+
+ public static int ASM_VERSION = Opcodes.ASM9;
+
+ public static byte[] transformInvoke(byte[] bytes, MethodTransformer transformer) {
+ ClassReader reader = new ClassReader(bytes);
+ ClassWriter writer = new ClassWriter(reader, 0);
+ ClassVisitor subvisitor = new InvokeTransformer(writer, transformer);
+ reader.accept(subvisitor, 0);
+ return writer.toByteArray();
+ }
+
+ public static class InvokeTransformer extends ClassVisitor {
+
+ private final MethodTransformer transformer;
+
+ InvokeTransformer(ClassWriter writer, MethodTransformer transformer) {
+ super(ASM_VERSION, writer);
+ this.transformer = transformer;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor sub = super.visitMethod(access, name, descriptor, signature, exceptions);
+ transformer.setMv(sub);
+ return transformer;
+ }
+ }
+
+ public static class MethodTransformer extends MethodVisitor {
+
+ protected MethodTransformer(int api) {
+ super(api);
+ }
+
+ public void setMv(MethodVisitor visitor) {
+ this.mv = visitor;
+ }
+ }
+}
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
new file mode 100644
index 0000000..137ac12
--- /dev/null
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -0,0 +1,54 @@
+// 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 desugaredlibrary;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class CustomConversionAsmRewriteDescription {
+
+ static final String WRAP_CONVERT = "wrap_convert";
+ static final String INVERTED_WRAP_CONVERT = "inverted_wrap_convert";
+ static final String CONVERT = "convert";
+
+ private static final Set<String> ENUM_WRAP_CONVERT_OWNER =
+ ImmutableSet.of(
+ "j$/nio/file/StandardOpenOption",
+ "j$/nio/file/LinkOption",
+ "j$/nio/file/attribute/PosixFilePermission",
+ "j$/util/stream/Collector$Characteristics");
+ private static final Set<String> WRAP_CONVERT_OWNER =
+ ImmutableSet.of(
+ "j$/nio/file/spi/FileSystemProvider",
+ "j$/nio/file/spi/FileTypeDetector",
+ "j$/nio/file/Path",
+ "j$/nio/file/WatchEvent",
+ "j$/nio/file/attribute/BasicFileAttributes",
+ "j$/nio/file/attribute/BasicFileAttributeView",
+ "j$/nio/file/attribute/FileOwnerAttributeView",
+ "j$/nio/file/attribute/PosixFileAttributes",
+ "j$/nio/file/attribute/PosixFileAttributeView");
+
+ static Map<String, String> getJavaWrapConvertOwnerMap() {
+ return computeConvertOwnerMap("$VivifiedWrapper");
+ }
+
+ static Map<String, String> getJ$WrapConvertOwnerMap() {
+ return computeConvertOwnerMap("$Wrapper");
+ }
+
+ private static HashMap<String, String> computeConvertOwnerMap(String suffix) {
+ HashMap<String, String> map = new HashMap<>();
+ for (String theEnum : ENUM_WRAP_CONVERT_OWNER) {
+ map.put(theEnum, theEnum + "$EnumConversion");
+ }
+ for (String owner : WRAP_CONVERT_OWNER) {
+ map.put(owner, owner + suffix);
+ }
+ return map;
+ }
+}
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java
new file mode 100644
index 0000000..6107e67
--- /dev/null
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriter.java
@@ -0,0 +1,183 @@
+// 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 desugaredlibrary;
+
+import static desugaredlibrary.AsmRewriter.ASM_VERSION;
+import static desugaredlibrary.CustomConversionAsmRewriteDescription.CONVERT;
+import static desugaredlibrary.CustomConversionAsmRewriteDescription.INVERTED_WRAP_CONVERT;
+import static desugaredlibrary.CustomConversionAsmRewriteDescription.WRAP_CONVERT;
+import static desugaredlibrary.CustomConversionAsmRewriter.CustomConversionVersion.LEGACY;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+
+import com.google.common.io.ByteStreams;
+import desugaredlibrary.AsmRewriter.MethodTransformer;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class CustomConversionAsmRewriter {
+
+ public enum CustomConversionVersion {
+ LEGACY,
+ LATEST
+ }
+
+ public CustomConversionAsmRewriter(CustomConversionVersion legacy) {
+ this.legacy = legacy;
+ }
+
+ private final CustomConversionVersion legacy;
+ private final Map<String, String> javaWrapConvertOwnerMap =
+ CustomConversionAsmRewriteDescription.getJavaWrapConvertOwnerMap();
+ private final Map<String, String> j$WrapConvertOwnerMap =
+ CustomConversionAsmRewriteDescription.getJ$WrapConvertOwnerMap();
+
+ public static void generateJars(Path jar, Path outputDirectory) throws IOException {
+ for (CustomConversionVersion version : CustomConversionVersion.values()) {
+ new CustomConversionAsmRewriter(version).convert(jar, outputDirectory);
+ }
+ }
+
+ private void convert(Path jar, Path outputDirectory) throws IOException {
+ String fileName = jar.getFileName().toString();
+ String newFileName =
+ fileName.substring(0, fileName.length() - "_raw.jar".length())
+ + (legacy == LEGACY ? "_legacy" : "")
+ + ".jar";
+ Path convertedJar = outputDirectory.resolve(newFileName);
+ internalConvert(jar, convertedJar);
+ }
+
+ private void internalConvert(Path jar, Path convertedJar) throws IOException {
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out =
+ new ZipOutputStream(
+ new BufferedOutputStream(Files.newOutputStream(convertedJar, options)))) {
+ new CustomConversionAsmRewriter(legacy).convert(jar, out, legacy);
+ }
+ }
+
+ private void convert(
+ Path desugaredLibraryFiles, ZipOutputStream out, CustomConversionVersion legacy)
+ throws IOException {
+ try (ZipFile zipFile = new ZipFile(desugaredLibraryFiles.toFile(), StandardCharsets.UTF_8)) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ try (InputStream entryStream = zipFile.getInputStream(entry)) {
+ handleFile(entry, entryStream, out, legacy);
+ }
+ }
+ }
+ }
+
+ private void handleFile(
+ ZipEntry entry, InputStream input, ZipOutputStream out, CustomConversionVersion legacy)
+ throws IOException {
+ if (!entry.getName().endsWith(".class")) {
+ return;
+ }
+ if (legacy == LEGACY
+ && (entry.getName().contains("java.nio.file")
+ || entry.getName().contains("ApiFlips")
+ || entry.getName().contains("java.adapter"))) {
+ return;
+ }
+ final byte[] bytes = ByteStreams.toByteArray(input);
+ input.close();
+ final byte[] rewrittenBytes = transformInvoke(bytes);
+ writeToZipStream(out, entry.getName(), rewrittenBytes, ZipEntry.STORED);
+ }
+
+ public static void writeToZipStream(
+ ZipOutputStream stream, String entry, byte[] content, int compressionMethod)
+ throws IOException {
+ int offset = 0;
+ int length = content.length;
+ CRC32 crc = new CRC32();
+ crc.update(content, offset, length);
+ ZipEntry zipEntry = new ZipEntry(entry);
+ zipEntry.setMethod(compressionMethod);
+ zipEntry.setSize(length);
+ zipEntry.setCrc(crc.getValue());
+ zipEntry.setTime(0);
+ stream.putNextEntry(zipEntry);
+ stream.write(content, offset, length);
+ stream.closeEntry();
+ }
+
+ private byte[] transformInvoke(byte[] bytes) {
+ return AsmRewriter.transformInvoke(bytes, new CustomConversionRewriter(ASM_VERSION));
+ }
+
+ class CustomConversionRewriter extends MethodTransformer {
+
+ protected CustomConversionRewriter(int api) {
+ super(api);
+ }
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ if (opcode == INVOKESTATIC) {
+ if (name.equals(WRAP_CONVERT)) {
+ convertInvoke(
+ opcode,
+ owner,
+ descriptor,
+ isInterface,
+ javaWrapConvertOwnerMap,
+ j$WrapConvertOwnerMap);
+ return;
+ }
+ if (name.equals(INVERTED_WRAP_CONVERT)) {
+ convertInvoke(
+ opcode,
+ owner,
+ descriptor,
+ isInterface,
+ j$WrapConvertOwnerMap,
+ javaWrapConvertOwnerMap);
+ return;
+ }
+ }
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+
+ private void convertInvoke(
+ int opcode,
+ String owner,
+ String descriptor,
+ boolean isInterface,
+ Map<String, String> javaMap,
+ Map<String, String> j$Map) {
+ if (!javaMap.containsKey(owner)
+ || !j$Map.containsKey(owner)
+ || !(owner.startsWith("java") || owner.startsWith("j$"))) {
+ throw new RuntimeException("Cannot transform wrap_convert method for " + owner);
+ }
+ if (owner.startsWith("java")) {
+ String newOwner = j$Map.get(owner);
+ super.visitMethodInsn(opcode, newOwner, CONVERT, descriptor, isInterface);
+ return;
+ }
+ assert owner.startsWith("j$");
+ String newOwner = javaMap.get(owner);
+ super.visitMethodInsn(opcode, newOwner, CONVERT, descriptor, isInterface);
+ }
+ }
+}
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriterTask.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriterTask.java
new file mode 100644
index 0000000..bae3c1c
--- /dev/null
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriterTask.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 desugaredlibrary;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import javax.inject.Inject;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.workers.IsolationMode;
+import org.gradle.workers.WorkerExecutor;
+
+public class CustomConversionAsmRewriterTask extends DefaultTask {
+
+ private final WorkerExecutor workerExecutor;
+
+ private File rawJar;
+ private File outputDirectory;
+
+ @Inject
+ public CustomConversionAsmRewriterTask(WorkerExecutor workerExecutor) {
+ this.workerExecutor = workerExecutor;
+ }
+
+ @InputFile
+ public File getRawJar() {
+ return rawJar;
+ }
+
+ public void setRawJar(File rawJar) {
+ this.rawJar = rawJar;
+ }
+
+ @OutputDirectory
+ public File getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+
+ @TaskAction
+ void exec() {
+ workerExecutor.submit(
+ Run.class,
+ config -> {
+ config.setIsolationMode(IsolationMode.NONE);
+ config.params(rawJar, outputDirectory);
+ });
+ }
+
+ public static class Run implements Runnable {
+
+ private final File rawJar;
+ private final File outputDirectory;
+
+ @Inject
+ public Run(File rawJar, File outputDirectory) {
+ this.rawJar = rawJar;
+ this.outputDirectory = outputDirectory;
+ }
+
+ @Override
+ public void run() {
+ try {
+ CustomConversionAsmRewriter.generateJars(rawJar.toPath(), outputDirectory.toPath());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+}
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index ca4bc25..0586776 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -151,7 +151,7 @@
}
}
builders {
- name: "lib_desugar-archive"
+ name: "lib_desugar-archive-jdk11"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
dimensions: "cores:8"
@@ -167,6 +167,41 @@
'{'
' "builder_group": "internal.client.r8",'
' "recipe": "rex",'
+ ' "test_options": ['
+ ' "--variant=jdk11"'
+ ' ],'
+ ' "test_wrapper": "tools/archive_desugar_jdk_libs.py"'
+ '}'
+ priority: 25
+ execution_timeout_secs: 3600
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ }
+ builders {
+ name: "lib_desugar-archive-jdk8"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ exe {
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ cmd: "luciexe"
+ }
+ properties:
+ '{'
+ ' "builder_group": "internal.client.r8",'
+ ' "recipe": "rex",'
+ ' "test_options": ['
+ ' "--variant=jdk8"'
+ ' ],'
' "test_wrapper": "tools/archive_desugar_jdk_libs.py"'
'}'
priority: 25
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index e07a48e..9aaa005 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -121,9 +121,14 @@
short_name: "kotlin_old"
}
builders {
- name: "buildbucket/luci.r8.ci/lib_desugar-archive"
+ name: "buildbucket/luci.r8.ci/lib_desugar-archive-jdk11"
category: "library_desugar"
- short_name: "archive"
+ short_name: "jdk11"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/lib_desugar-archive-jdk8"
+ category: "library_desugar"
+ short_name: "jdk8"
}
builders {
name: "buildbucket/luci.r8.ci/desugared_library-head"
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 2c77bc7..10dd059 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -63,7 +63,7 @@
}
}
job {
- id: "lib_desugar-archive"
+ id: "lib_desugar-archive-jdk11"
realm: "ci"
acl_sets: "ci"
triggering_policy {
@@ -74,7 +74,22 @@
buildbucket {
server: "cr-buildbucket.appspot.com"
bucket: "ci"
- builder: "lib_desugar-archive"
+ builder: "lib_desugar-archive-jdk11"
+ }
+}
+job {
+ id: "lib_desugar-archive-jdk8"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 3
+ max_batch_size: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "ci"
+ builder: "lib_desugar-archive-jdk8"
}
}
job {
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index d1529cf..f93a678 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -240,12 +240,15 @@
dimensions = dimensions, category = category, release_trigger=release_trigger)
def archivers():
- for name in ["archive", "archive_release", "lib_desugar-archive"]:
+ for name in ["archive", "archive_release", "lib_desugar-archive-jdk11", "lib_desugar-archive-jdk8"]:
desugar = "desugar" in name
properties = {
"test_wrapper" : "tools/archive_desugar_jdk_libs.py" if desugar else "tools/archive.py",
"builder_group" : "internal.client.r8"
}
+ if desugar:
+ properties["test_options"] = ["--variant=jdk11" if "jdk11" in name else "--variant=jdk8"]
+
r8_builder(
name,
category = "library_desugar" if desugar else "archive",
diff --git a/src/library_desugar/java/j$/nio/file/Path.java b/src/library_desugar/java/j$/nio/file/Path.java
index 781ab5f..cb01ec0 100644
--- a/src/library_desugar/java/j$/nio/file/Path.java
+++ b/src/library_desugar/java/j$/nio/file/Path.java
@@ -6,7 +6,7 @@
public class Path {
- public static j$.nio.file.Path wrap_convert(java.nio.file.Path path) {
+ public static j$.nio.file.Path inverted_wrap_convert(java.nio.file.Path path) {
return null;
}
}
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
index da93736..af2c8f7 100644
--- a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
+++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -29,7 +29,7 @@
static class PlatformFileTypeDetector extends FileTypeDetector {
@Override
public String probeContentType(Path path) throws IOException {
- return j$.nio.file.Files.probeContentType(j$.nio.file.Path.wrap_convert(path));
+ return j$.nio.file.Files.probeContentType(j$.nio.file.Path.inverted_wrap_convert(path));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
deleted file mode 100644
index 13cd385..0000000
--- a/src/main/java/com/android/tools/r8/CompatDxHelper.java
+++ /dev/null
@@ -1,28 +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.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-
-public class CompatDxHelper {
- public static void run(D8Command command, Boolean minimalMainDex)
- throws CompilationFailedException {
- AndroidApp app = command.getInputApp();
- InternalOptions options = command.getInternalOptions();
- // DX allows --multi-dex without specifying a main dex list for legacy devices.
- // That is broken, but for CompatDX we do the same to not break existing builds
- // that are trying to transition.
- options.enableMainDexListCheck = false;
- // DX has a minimal main dex flag. In compat mode only do minimal main dex
- // if the flag is actually set.
- options.minimalMainDex = minimalMainDex;
- D8.runForTesting(app, options);
- }
-
- public static void ignoreDexInArchive(BaseCommand.Builder builder) {
- builder.setIgnoreDexInArchive(true);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index b638235..80f679b 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -15,7 +15,6 @@
boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
setProguardCompatibility(forceProguardCompatibility);
- setIgnoreDexInArchive(true);
}
public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
@@ -26,6 +25,5 @@
boolean forceProguardCompatibility, boolean disableVerticalClassMerging) {
setProguardCompatibility(forceProguardCompatibility);
setDisableVerticalClassMerging(disableVerticalClassMerging);
- setIgnoreDexInArchive(true);
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 223fc66..4cc8c25 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -442,7 +442,7 @@
assert appView.appInfo().hasLiveness();
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- assert new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
+ new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
new StartupInstrumentation(appView).instrumentAllClasses(executorService);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 7923102..1496f40 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -127,14 +127,17 @@
Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
+ setIgnoreDexInArchive(true);
}
private Builder(AndroidApp app) {
super(app);
+ setIgnoreDexInArchive(true);
}
private Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
super(app, diagnosticsHandler);
+ setIgnoreDexInArchive(true);
}
// Internal
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
index 0539dcf..c788d6b 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.CompatByteBuffer;
-import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -92,7 +92,9 @@
InternalOptions options, DiagnosticsHandler diagnosticsHandler) {
URL resource = AndroidApiDataAccess.class.getClassLoader().getResource(RESOURCE_NAME);
if (resource == null) {
- throw new CompilationError("Could not find the api database at " + RESOURCE_NAME);
+ diagnosticsHandler.warning(
+ new StringDiagnostic("Could not find the api database at " + RESOURCE_NAME));
+ return new AndroidApiDataAccessNoBacking();
}
if (options.apiModelingOptions().useMemoryMappedByteBuffer) {
try {
@@ -126,11 +128,14 @@
try (InputStream apiInputStream =
AndroidApiDataAccess.class.getClassLoader().getResourceAsStream(RESOURCE_NAME)) {
if (apiInputStream == null) {
- throw new CompilationError("Could not find the api database at: " + resource);
+ diagnosticsHandler.warning(
+ new StringDiagnostic("Could not open the api database at " + RESOURCE_NAME));
+ return new AndroidApiDataAccessNoBacking();
}
return new AndroidApiDataAccessInMemory(ByteStreams.toByteArray(apiInputStream));
} catch (IOException e) {
- throw new CompilationError("Could not read the api database.", e);
+ diagnosticsHandler.warning(new ExceptionDiagnostic(e));
+ return new AndroidApiDataAccessNoBacking();
}
}
@@ -306,6 +311,10 @@
serialized);
}
+ public boolean isNoBacking() {
+ return false;
+ }
+
public static class AndroidApiDataAccessByteMapped extends AndroidApiDataAccess {
private final CompatByteBuffer mappedByteBuffer;
@@ -437,4 +446,38 @@
return 0;
}
}
+
+ public static class AndroidApiDataAccessNoBacking extends AndroidApiDataAccess {
+
+ @Override
+ int readConstantPoolSize() {
+ throw new Unreachable();
+ }
+
+ @Override
+ PositionAndLength readPositionAndLength(int offset) {
+ throw new Unreachable();
+ }
+
+ @Override
+ boolean payloadHasConstantPoolValue(int offset, int length, byte[] value) {
+ throw new Unreachable();
+ }
+
+ @Override
+ int payloadContainsConstantPoolValue(
+ int offset, int length, byte[] value, BiPredicate<Integer, byte[]> predicate) {
+ throw new Unreachable();
+ }
+
+ @Override
+ byte readApiLevelForPayloadOffset(int offset, int length, byte[] value) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isNoBacking() {
+ return true;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index 22d0c6d..2c6d00d 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -175,11 +175,6 @@
return lookupApiLevel(field);
}
- private int getConstantPoolId(DexString string) {
- return constantPoolCache.computeIfAbsent(
- string, key -> getDataAccess(options, diagnosticsHandler).getConstantPoolIndex(string));
- }
-
private AndroidApiLevel lookupApiLevel(DexReference reference) {
// We use Android platform to track if an element is unknown since no occurrences of that api
// level exists in the database.
@@ -187,10 +182,21 @@
lookupCache.computeIfAbsent(
reference,
ref -> {
+ // Prefetch the data access
+ if (dataAccess == null) {
+ getDataAccess(options, diagnosticsHandler);
+ }
+ if (dataAccess.isNoBacking()) {
+ return ANDROID_PLATFORM;
+ }
byte[] uniqueDescriptorForReference;
try {
uniqueDescriptorForReference =
- getUniqueDescriptorForReference(ref, this::getConstantPoolId);
+ getUniqueDescriptorForReference(
+ ref,
+ string ->
+ constantPoolCache.computeIfAbsent(
+ string, key -> dataAccess.getConstantPoolIndex(string)));
} catch (Exception e) {
uniqueDescriptorForReference = getNonExistingDescriptor();
}
@@ -198,8 +204,7 @@
return ANDROID_PLATFORM;
} else {
byte apiLevelForReference =
- getDataAccess(options, diagnosticsHandler)
- .getApiLevelForReference(uniqueDescriptorForReference, ref);
+ dataAccess.getApiLevelForReference(uniqueDescriptorForReference, ref);
return (apiLevelForReference <= 0)
? ANDROID_PLATFORM
: AndroidApiLevel.getAndroidApiLevel(apiLevelForReference);
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 d1b5e9e..e612d68 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -545,13 +545,17 @@
return frameTypeType() + "." + frameType.asPrimitive().getTypeName() + "Type()";
}
} else {
- assert frameType.isInitialized();
+ assert frameType.isInitializedReferenceType();
+ assert !frameType.isInitializedNonNullReferenceTypeWithInterfaces()
+ : "Unexpected InitializedNonNullReferenceTypeWithInterfaces in CfFrame";
if (frameType.isNullType()) {
- return frameTypeType() + ".initialized(" + dexItemFactoryType() + ".nullValueType)";
+ return frameTypeType() + ".nullType()";
} else {
+ assert frameType.isInitializedNonNullReferenceTypeWithoutInterfaces();
return frameTypeType()
- + ".initialized("
- + dexType(frameType.asInitializedReferenceType().getInitializedType())
+ + ".initializedNonNullReference("
+ + dexType(
+ frameType.asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType())
+ ")";
}
}
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 c3dac32..5869f46 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -68,6 +68,7 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Monitor;
@@ -457,7 +458,16 @@
private void print(FrameType type) {
if (type.isInitializedReferenceType()) {
- appendType(type.asInitializedReferenceType().getInitializedType());
+ if (type.isNullType()) {
+ builder.append("null");
+ } else if (type.isInitializedNonNullReferenceTypeWithInterfaces()) {
+ appendTypeElement(
+ type.asInitializedNonNullReferenceTypeWithInterfaces()
+ .getInitializedTypeWithInterfaces());
+ } else {
+ assert type.isInitializedNonNullReferenceTypeWithoutInterfaces();
+ appendType(type.asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType());
+ }
} else if (type.isUninitializedNew()) {
builder.append("uninitialized ").append(getLabel(type.getUninitializedLabel()));
} else {
@@ -775,6 +785,10 @@
}
}
+ private void appendTypeElement(TypeElement type) {
+ builder.append(type.toString());
+ }
+
private void appendClass(DexType type) {
assert type.isArrayType() || type.isClassType();
if (mapper == null) {
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 0bae2fd..bbffacb 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
@@ -115,14 +115,24 @@
(state, head) -> {
if (head.isNullType()) {
return getType() == MemberType.OBJECT
- ? state.push(config, FrameType.initialized(DexItemFactory.nullValueType))
+ ? state.push(config, FrameType.nullType())
: state.push(appView, config, getType());
}
- return state.push(
- config,
- head.asInitializedReferenceType()
- .getInitializedType()
- .toArrayElementType(dexItemFactory));
+ if (head.isInitializedNonNullReferenceTypeWithInterfaces()) {
+ return state.push(
+ config,
+ head.asInitializedNonNullReferenceTypeWithInterfaces()
+ .getInitializedTypeWithInterfaces()
+ .asArrayType()
+ .getMemberType());
+ } else {
+ assert head.isInitializedNonNullReferenceTypeWithoutInterfaces();
+ return state.push(
+ config,
+ head.asInitializedNonNullReferenceTypeWithoutInterfaces()
+ .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 918203f..41f6774 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
@@ -36,6 +36,16 @@
return getStoreType();
}
+ @Override
+ public boolean isArrayStore() {
+ return true;
+ }
+
+ @Override
+ public CfArrayStore asArrayStore() {
+ return this;
+ }
+
private int getStoreType() {
switch (getType()) {
case OBJECT:
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 5def6cf..6858fe9 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
@@ -66,10 +66,11 @@
if (source.isInitialized()) {
// Both are instantiated types and we resort to primitive type/java type hierarchy checking.
return isAssignable(
- source.asInitializedReferenceType().getInitializedType(),
- target.asInitializedReferenceType().getInitializedType());
+ source.asInitializedReferenceType().getInitializedType(dexItemFactory),
+ target.asInitializedReferenceType().getInitializedType(dexItemFactory));
}
- return target.asInitializedReferenceType().getInitializedType() == dexItemFactory.objectType;
+ return target.asInitializedReferenceType().getInitializedType(dexItemFactory)
+ == dexItemFactory.objectType;
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 80813e1..9ff6d09 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -77,18 +77,6 @@
return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
}
- public boolean isFieldGet() {
- return false;
- }
-
- public boolean isStaticFieldGet() {
- return false;
- }
-
- public boolean isStaticFieldPut() {
- return false;
- }
-
public abstract CfFieldInstruction createWithField(DexField field);
@Override
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 f989239..6b2ee79 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
@@ -259,11 +259,10 @@
}
private void internalRegisterUse(UseRegistry<?> registry, FrameType frameType) {
- if (frameType.isInitializedReferenceType()) {
- if (frameType.isNullType()) {
- return;
- }
- registry.registerTypeReference(frameType.asInitializedReferenceType().getInitializedType());
+ assert !frameType.isInitializedNonNullReferenceTypeWithInterfaces();
+ if (frameType.isInitializedNonNullReferenceTypeWithoutInterfaces()) {
+ registry.registerTypeReference(
+ frameType.asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType());
} else if (frameType.isUninitializedNew()) {
registry.registerTypeReference(frameType.asUninitializedNew().getUninitializedNewType());
}
@@ -303,12 +302,12 @@
public static PreciseFrameType getInitializedFrameType(
UninitializedFrameType unInit, UninitializedFrameType other, DexType newType) {
if (unInit.isUninitializedThis() && other.isUninitializedThis()) {
- return FrameType.initialized(newType);
+ return FrameType.initializedNonNullReference(newType);
}
if (unInit.isUninitializedNew()
&& other.isUninitializedNew()
&& unInit.getUninitializedLabel() == other.getUninitializedLabel()) {
- return FrameType.initialized(newType);
+ return FrameType.initializedNonNullReference(newType);
}
return other;
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
deleted file mode 100644
index 1a62143..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ /dev/null
@@ -1,220 +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.cf.code;
-
-import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
-import com.android.tools.r8.cf.code.frame.FrameType;
-import com.android.tools.r8.cf.code.frame.PreciseFrameType;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.CfCodeDiagnostics;
-import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
-import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
-import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.collections.ImmutableDeque;
-import com.google.common.collect.Sets;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-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;
- private final ProgramMethod method;
- private final DexMethod previousMethod;
-
- private final Map<CfLabel, CfFrame> stateMap;
- private final List<CfTryCatch> tryCatchRanges;
-
- private final Deque<CfTryCatch> currentCatchRanges = new ArrayDeque<>();
- private final Set<CfLabel> tryCatchRangeLabels;
-
- public CfFrameVerificationHelper(
- AppView<?> appView,
- CfCode code,
- GraphLens codeLens,
- ProgramMethod method,
- Map<CfLabel, CfFrame> stateMap,
- List<CfTryCatch> tryCatchRanges) {
- this.appView = appView;
- this.assignability = new CfAssignability(appView);
- this.code = code;
- this.codeLens = codeLens;
- this.method = method;
- this.previousMethod =
- appView.graphLens().getOriginalMethodSignature(method.getReference(), codeLens);
- this.stateMap = stateMap;
- this.tryCatchRanges = tryCatchRanges;
- this.factory = appView.dexItemFactory();
- // Compute all labels that marks a start or end to catch ranges.
- tryCatchRangeLabels = Sets.newIdentityHashSet();
- for (CfTryCatch tryCatchRange : tryCatchRanges) {
- tryCatchRangeLabels.add(tryCatchRange.start);
- tryCatchRangeLabels.add(tryCatchRange.end);
- }
- }
-
- @Override
- public CfAssignability getAssignability() {
- return assignability;
- }
-
- @Override
- public DexMethod getCurrentContext() {
- return previousMethod;
- }
-
- @Override
- public int getMaxLocals() {
- return code.getMaxLocals();
- }
-
- @Override
- public int getMaxStack() {
- return code.getMaxStack();
- }
-
- @Override
- public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
- // If the code is rewritten according to the graph lens, we perform a strict check that the
- // given type is the same as the current holder's super class.
- if (codeLens == appView.graphLens()) {
- return type == method.getHolder().getSuperType();
- }
- // Otherwise, we don't know what the super class of the current class was at the point of the
- // code lens. We return true, which has the consequence that we may accept a constructor call
- // for an uninitialized-this value where the constructor is not defined in the immediate parent
- // class.
- return true;
- }
-
- public void seenLabel(CfLabel label) {
- if (tryCatchRangeLabels.contains(label)) {
- for (CfTryCatch tryCatchRange : tryCatchRanges) {
- if (tryCatchRange.start == label) {
- currentCatchRanges.add(tryCatchRange);
- }
- }
- currentCatchRanges.removeIf(currentRange -> currentRange.end == label);
- }
- }
-
- public CfCodeDiagnostics checkTryCatchRanges() {
- for (CfTryCatch tryCatchRange : tryCatchRanges) {
- CfCodeDiagnostics diagnostics = checkTryCatchRange(tryCatchRange);
- if (diagnostics != null) {
- return diagnostics;
- }
- }
- return null;
- }
-
- public CfCodeDiagnostics checkTryCatchRange(CfTryCatch tryCatchRange) {
- // According to the spec:
- // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1
- // saying ` and the handler's target (the initial instruction of the handler code) is type
- // safe assuming an incoming type state T. The type state T is derived from ExcStackFrame
- // by replacing the operand stack with a stack whose sole element is the handler's
- // exception class.
- for (CfLabel target : tryCatchRange.getTargets()) {
- CfFrame destinationFrame = stateMap.get(target);
- if (destinationFrame == null) {
- return CfCodeStackMapValidatingException.invalidTryCatchRange(
- method, tryCatchRange, "No frame for target catch range target", appView);
- }
- // From the spec: the handler's exception class is assignable to the class Throwable.
- for (DexType guard : tryCatchRange.guards) {
- if (!assignability.isAssignable(guard, factory.throwableType)) {
- return CfCodeStackMapValidatingException.invalidTryCatchRange(
- method,
- tryCatchRange,
- "Could not assign " + guard.getTypeName() + " to java.lang.Throwable",
- appView);
- }
- Deque<PreciseFrameType> sourceStack = ImmutableDeque.of(FrameType.initialized(guard));
- AssignabilityResult assignabilityResult =
- assignability.isStackAssignable(sourceStack, destinationFrame.getStack());
- if (assignabilityResult.isFailed()) {
- return CfCodeStackMapValidatingException.invalidTryCatchRange(
- method, tryCatchRange, assignabilityResult.asFailed().getMessage(), appView);
- }
- }
- }
- return null;
- }
-
- public CfFrameState checkExceptionEdges(CfFrameState state) {
- for (CfTryCatch currentCatchRange : currentCatchRanges) {
- for (CfLabel target : currentCatchRange.getTargets()) {
- CfFrame destinationFrame = stateMap.get(target);
- if (destinationFrame == null) {
- return CfFrameState.error("No frame for target catch range target");
- }
- state = state.checkLocals(this, destinationFrame);
- }
- }
- return state;
- }
-
- public CfFrameState checkTarget(CfFrameState state, CfLabel label) {
- CfFrame destinationFrame = getDestinationFrame(label);
- return destinationFrame != null
- ? state.checkLocals(this, destinationFrame).checkStack(this, destinationFrame)
- : CfFrameState.error("No destination frame");
- }
-
- private CfFrame getDestinationFrame(CfLabel label) {
- return stateMap.get(label);
- }
-
- public TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeStateForNextInstruction(
- CfInstruction instruction, int instructionIndex, CfFrameState state) {
- if (!instruction.isJump()) {
- return TraversalContinuation.doContinue(state);
- }
- if (instructionIndex == code.getInstructions().size() - 1) {
- return TraversalContinuation.doContinue(CfFrameState.bottom());
- }
- if (instructionIndex == code.getInstructions().size() - 2
- && code.getInstructions().get(instructionIndex + 1).isLabel()) {
- return TraversalContinuation.doContinue(CfFrameState.bottom());
- }
- if (instruction.asJump().hasFallthrough()) {
- return TraversalContinuation.doContinue(state);
- }
- int nextInstructionIndex = instructionIndex + 1;
- CfInstruction nextInstruction = code.getInstructions().get(nextInstructionIndex);
- CfFrame nextFrame = null;
- if (nextInstruction.isFrame()) {
- nextFrame = nextInstruction.asFrame();
- } else if (nextInstruction.isLabel()) {
- nextFrame = getDestinationFrame(nextInstruction.asLabel());
- }
- if (nextFrame != null) {
- CfFrame currentFrameCopy = nextFrame.mutableCopy();
- return TraversalContinuation.doContinue(
- new ConcreteCfFrameState(
- currentFrameCopy.getMutableLocals(),
- currentFrameCopy.getMutableStack(),
- currentFrameCopy.computeStackSize()));
- }
- return TraversalContinuation.doBreak(
- CfCodeStackMapValidatingException.invalidStackMapForInstruction(
- method, nextInstructionIndex, nextInstruction, "Expected frame instruction", appView));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
new file mode 100644
index 0000000..7866b2d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -0,0 +1,455 @@
+// 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.cf.code;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
+import com.android.tools.r8.cf.code.frame.FrameType;
+import com.android.tools.r8.cf.code.frame.PreciseFrameType;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.CfCodeDiagnostics;
+import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
+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.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
+import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
+import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+public class CfFrameVerifier {
+
+ private final AppView<?> appView;
+ private final CfCode code;
+ private final CfAnalysisConfig config;
+ private final CfFrameVerifierEventConsumer eventConsumer;
+ private final DexItemFactory factory;
+ private final ProgramMethod method;
+ private final Optional<DexMethod> previousMethod;
+ private final boolean previousMethodIsInstance;
+
+ private final Deque<CfTryCatch> activeCatchHandlers = new ArrayDeque<>();
+ private final Set<CfLabel> tryCatchRangeLabels;
+
+ public CfFrameVerifier(
+ AppView<?> appView,
+ CfCode code,
+ CfAnalysisConfig config,
+ CfFrameVerifierEventConsumer eventConsumer,
+ ProgramMethod method,
+ Optional<DexMethod> previousMethod,
+ boolean previousMethodIsInstance) {
+ this.appView = appView;
+ this.code = code;
+ this.config = config;
+ this.eventConsumer = eventConsumer;
+ this.factory = appView.dexItemFactory();
+ this.method = method;
+ this.previousMethod = previousMethod;
+ this.previousMethodIsInstance = previousMethodIsInstance;
+ this.tryCatchRangeLabels = code.getTryCatchRangeLabels();
+ }
+
+ public static Builder builder(AppView<?> appView, CfCode code, ProgramMethod method) {
+ return new Builder(appView, code, method);
+ }
+
+ public StackMapStatus run() {
+ if (!appView.options().canUseInputStackMaps()
+ || appView.options().testing.disableStackMapVerification) {
+ return StackMapStatus.NOT_PRESENT;
+ }
+
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.hasClassFileVersion()
+ && definition.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
+ return StackMapStatus.NOT_PRESENT;
+ }
+
+ // Build a map from labels to frames.
+ TraversalContinuation<CfCodeDiagnostics, Map<CfLabel, CfFrame>> labelToFrameMapOrError =
+ buildLabelToFrameMap();
+ if (labelToFrameMapOrError.shouldBreak()) {
+ return fail(labelToFrameMapOrError);
+ }
+ Map<CfLabel, CfFrame> labelToFrameMap = labelToFrameMapOrError.asContinue().getValue();
+
+ // Check try catch ranges.
+ CfCodeDiagnostics diagnostics = checkTryCatchRanges(labelToFrameMap);
+ if (diagnostics != null) {
+ return fail(diagnostics);
+ }
+
+ // Compute initial state.
+ TraversalContinuation<CfCodeDiagnostics, CfFrameState> initialState = computeInitialState();
+ if (initialState.shouldBreak()) {
+ return fail(initialState);
+ }
+
+ // Linear scan over instructions.
+ CfFrameState state = initialState.asContinue().getValue();
+ for (int i = 0; i < code.getInstructions().size(); i++) {
+ CfInstruction instruction = code.getInstruction(i);
+ assert !state.isError();
+ // Check the exceptional edge prior to evaluating the instruction. The local state is stable
+ // at this point as store operations are not throwing and the current stack does not
+ // affect the exceptional transfer (the exception edge is always a singleton stack).
+ if (instruction.canThrow()) {
+ assert !instruction.isStore();
+ state = checkExceptionEdges(state, labelToFrameMap);
+ }
+ if (instruction.isLabel()) {
+ updateActiveCatchHandlers(instruction.asLabel());
+ }
+ eventConsumer.acceptInstructionState(instruction, state);
+ state = instruction.evaluate(state, appView, config);
+ if (instruction.isJumpWithNormalTarget()) {
+ CfInstruction fallthroughInstruction =
+ (i + 1) < code.getInstructions().size() ? code.getInstruction(i + 1) : null;
+ TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
+ instruction.traverseNormalTargets(
+ (target, currentState) -> {
+ if (target != fallthroughInstruction) {
+ assert target.isLabel();
+ currentState = checkTarget(currentState, target.asLabel(), labelToFrameMap);
+ }
+ return TraversalContinuation.doContinue(currentState);
+ },
+ fallthroughInstruction,
+ state);
+ state = traversalContinuation.asContinue().getValue();
+ }
+ TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
+ computeStateForNextInstruction(instruction, i, state, labelToFrameMap);
+ if (traversalContinuation.isContinue()) {
+ state = traversalContinuation.asContinue().getValue();
+ } else {
+ return fail(traversalContinuation);
+ }
+ if (state.isError()) {
+ return fail(
+ CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+ method, i, instruction, state.asError().getMessage(), appView));
+ }
+ }
+ return StackMapStatus.VALID;
+ }
+
+ private TraversalContinuation<CfCodeDiagnostics, Map<CfLabel, CfFrame>> buildLabelToFrameMap() {
+ Map<CfLabel, CfFrame> labelToFrameMap = new IdentityHashMap<>();
+ List<CfLabel> labels = new ArrayList<>();
+ boolean requireStackMapFrame = !code.getTryCatchRanges().isEmpty();
+ for (CfInstruction instruction : code.getInstructions()) {
+ if (instruction.isFrame()) {
+ CfFrame frame = instruction.asFrame();
+ if (!labels.isEmpty()) {
+ for (CfLabel label : labels) {
+ if (labelToFrameMap.containsKey(label)) {
+ return TraversalContinuation.doBreak(
+ CfCodeStackMapValidatingException.multipleFramesForLabel(method, appView));
+ }
+ labelToFrameMap.put(label, frame);
+ }
+ } else if (instruction != code.getInstruction(0)) {
+ // From b/168212806, it is possible that the first instruction is a frame.
+ return TraversalContinuation.doBreak(
+ CfCodeStackMapValidatingException.unexpectedStackMapFrame(method, appView));
+ }
+ }
+ // We are trying to map a frame to a label, but we can have positions in between, so skip
+ // those.
+ if (instruction.isPosition()) {
+ continue;
+ } else if (instruction.isLabel()) {
+ labels.add(instruction.asLabel());
+ } else {
+ labels.clear();
+ }
+ if (!requireStackMapFrame) {
+ requireStackMapFrame = instruction.isJump() && !isFinalAndExitInstruction(instruction);
+ }
+ }
+ // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
+ if (requireStackMapFrame && labelToFrameMap.isEmpty()) {
+ return TraversalContinuation.doBreak(
+ CfCodeStackMapValidatingException.noFramesForMethodWithJumps(method, appView));
+ }
+ return TraversalContinuation.doContinue(labelToFrameMap);
+ }
+
+ private StackMapStatus fail(TraversalContinuation<CfCodeDiagnostics, ?> traversalContinuation) {
+ assert traversalContinuation.shouldBreak();
+ return fail(traversalContinuation.asBreak().getValue());
+ }
+
+ private StackMapStatus fail(CfCodeDiagnostics diagnostics) {
+ eventConsumer.acceptError(diagnostics);
+ return StackMapStatus.INVALID;
+ }
+
+ private void updateActiveCatchHandlers(CfLabel label) {
+ if (tryCatchRangeLabels.contains(label)) {
+ for (CfTryCatch tryCatchRange : code.getTryCatchRanges()) {
+ if (tryCatchRange.start == label) {
+ activeCatchHandlers.add(tryCatchRange);
+ }
+ }
+ activeCatchHandlers.removeIf(currentRange -> currentRange.end == label);
+ }
+ }
+
+ private CfCodeDiagnostics checkTryCatchRanges(Map<CfLabel, CfFrame> labelToFrameMap) {
+ for (CfTryCatch tryCatchRange : code.getTryCatchRanges()) {
+ CfCodeDiagnostics diagnostics = checkTryCatchRange(tryCatchRange, labelToFrameMap);
+ if (diagnostics != null) {
+ return diagnostics;
+ }
+ }
+ return null;
+ }
+
+ private CfCodeDiagnostics checkTryCatchRange(
+ CfTryCatch tryCatchRange, Map<CfLabel, CfFrame> labelToFrameMap) {
+ // According to the spec:
+ // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1
+ // saying ` and the handler's target (the initial instruction of the handler code) is type
+ // safe assuming an incoming type state T. The type state T is derived from ExcStackFrame
+ // by replacing the operand stack with a stack whose sole element is the handler's
+ // exception class.
+ for (CfLabel target : tryCatchRange.getTargets()) {
+ CfFrame destinationFrame = labelToFrameMap.get(target);
+ if (destinationFrame == null) {
+ return CfCodeStackMapValidatingException.invalidTryCatchRange(
+ method, tryCatchRange, "No frame for target catch range target", appView);
+ }
+ // From the spec: the handler's exception class is assignable to the class Throwable.
+ for (DexType guard : tryCatchRange.guards) {
+ if (!config.getAssignability().isAssignable(guard, factory.throwableType)) {
+ return CfCodeStackMapValidatingException.invalidTryCatchRange(
+ method,
+ tryCatchRange,
+ "Could not assign " + guard.getTypeName() + " to java.lang.Throwable",
+ appView);
+ }
+ Deque<PreciseFrameType> sourceStack =
+ ImmutableDeque.of(FrameType.initializedNonNullReference(guard));
+ AssignabilityResult assignabilityResult =
+ config.getAssignability().isStackAssignable(sourceStack, destinationFrame.getStack());
+ if (assignabilityResult.isFailed()) {
+ return CfCodeStackMapValidatingException.invalidTryCatchRange(
+ method, tryCatchRange, assignabilityResult.asFailed().getMessage(), appView);
+ }
+ }
+ }
+ return null;
+ }
+
+ private CfFrameState checkExceptionEdges(
+ CfFrameState state, Map<CfLabel, CfFrame> labelToFrameMap) {
+ for (CfTryCatch currentCatchRange : activeCatchHandlers) {
+ for (CfLabel target : currentCatchRange.getTargets()) {
+ CfFrame destinationFrame = labelToFrameMap.get(target);
+ if (destinationFrame == null) {
+ return CfFrameState.error("No frame for target catch range target");
+ }
+ state = state.checkLocals(config, destinationFrame);
+ }
+ }
+ return state;
+ }
+
+ private CfFrameState checkTarget(
+ CfFrameState state, CfLabel label, Map<CfLabel, CfFrame> labelToFrameMap) {
+ CfFrame destinationFrame = labelToFrameMap.get(label);
+ return destinationFrame != null
+ ? state.checkLocals(config, destinationFrame).checkStack(config, destinationFrame)
+ : CfFrameState.error("No destination frame");
+ }
+
+ private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeInitialState() {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ CfFrameState state = new ConcreteCfFrameState();
+ int localIndex = 0;
+ DexMethod context = previousMethod.orElse(method.getReference());
+ if (method.getDefinition().isInstance() || previousMethodIsInstance) {
+ state =
+ state.storeLocal(
+ localIndex,
+ context.isInstanceInitializer(dexItemFactory)
+ || context.mustBeInlinedIntoInstanceInitializer(appView)
+ || context.isHorizontallyMergedInstanceInitializer(dexItemFactory)
+ ? FrameType.uninitializedThis()
+ : FrameType.initializedNonNullReference(context.getHolderType()),
+ config);
+ localIndex++;
+ }
+ for (DexType parameter : context.getParameters()) {
+ state = state.storeLocal(localIndex, FrameType.initialized(parameter), config);
+ localIndex += parameter.getRequiredRegisters();
+ }
+ if (state.isError()) {
+ return TraversalContinuation.doBreak(
+ CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+ method, 0, code.getInstruction(0), state.asError().getMessage(), appView));
+ }
+ return TraversalContinuation.doContinue(state);
+ }
+
+ private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeStateForNextInstruction(
+ CfInstruction instruction,
+ int instructionIndex,
+ CfFrameState state,
+ Map<CfLabel, CfFrame> labelToFrameMap) {
+ if (!instruction.isJump()) {
+ return TraversalContinuation.doContinue(state);
+ }
+ if (instructionIndex == code.getInstructions().size() - 1) {
+ return TraversalContinuation.doContinue(CfFrameState.bottom());
+ }
+ if (instructionIndex == code.getInstructions().size() - 2
+ && code.getInstruction(instructionIndex + 1).isLabel()) {
+ return TraversalContinuation.doContinue(CfFrameState.bottom());
+ }
+ if (instruction.asJump().hasFallthrough()) {
+ return TraversalContinuation.doContinue(state);
+ }
+ int nextInstructionIndex = instructionIndex + 1;
+ CfInstruction nextInstruction = code.getInstruction(nextInstructionIndex);
+ CfFrame nextFrame = null;
+ if (nextInstruction.isFrame()) {
+ nextFrame = nextInstruction.asFrame();
+ } else if (nextInstruction.isLabel()) {
+ nextFrame = labelToFrameMap.get(nextInstruction.asLabel());
+ }
+ if (nextFrame != null) {
+ CfFrame currentFrameCopy = nextFrame.mutableCopy();
+ return TraversalContinuation.doContinue(
+ new ConcreteCfFrameState(
+ currentFrameCopy.getMutableLocals(),
+ currentFrameCopy.getMutableStack(),
+ currentFrameCopy.computeStackSize()));
+ }
+ return TraversalContinuation.doBreak(
+ CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+ method, nextInstructionIndex, nextInstruction, "Expected frame instruction", appView));
+ }
+
+ private boolean isFinalAndExitInstruction(CfInstruction instruction) {
+ boolean isReturnOrThrow = instruction.isThrow() || instruction.isReturn();
+ if (!isReturnOrThrow) {
+ return false;
+ }
+ for (int i = code.getInstructions().size() - 1; i >= 0; i--) {
+ CfInstruction instr = code.getInstruction(i);
+ if (instr == instruction) {
+ return true;
+ }
+ if (instr.isPosition() || instr.isLabel()) {
+ continue;
+ }
+ return false;
+ }
+ throw new Unreachable("Instruction " + instruction + " should be in instructions");
+ }
+
+ public enum StackMapStatus {
+ NOT_VERIFIED,
+ NOT_PRESENT,
+ INVALID,
+ VALID;
+
+ public boolean isNotPresent() {
+ return this == NOT_PRESENT;
+ }
+
+ public boolean isValid() {
+ return this == VALID;
+ }
+
+ public boolean isValidOrNotPresent() {
+ return this == VALID || this == NOT_PRESENT;
+ }
+
+ public boolean isInvalidOrNotPresent() {
+ return this == INVALID || this == NOT_PRESENT;
+ }
+ }
+
+ public static class Builder {
+
+ private final AppView<?> appView;
+ private final CfCode code;
+ private final ProgramMethod method;
+
+ private CfAnalysisConfig config;
+ private CfFrameVerifierEventConsumer eventConsumer;
+ private Optional<DexMethod> previousMethod = Optional.empty();
+ private boolean previousMethodIsInstance;
+
+ Builder(AppView<?> appView, CfCode code, ProgramMethod method) {
+ this.appView = appView;
+ this.code = code;
+ this.method = method;
+ }
+
+ public Builder setCodeLens(GraphLens codeLens) {
+ if (codeLens != appView.graphLens()) {
+ this.previousMethod =
+ Optional.of(
+ appView.graphLens().getOriginalMethodSignature(method.getReference(), codeLens));
+ this.previousMethodIsInstance =
+ method.getDefinition().isInstance()
+ || appView
+ .graphLens()
+ .lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens)
+ .getArgumentInfoCollection()
+ .isConvertedToStaticMethod();
+ }
+ return this;
+ }
+
+ public Builder setConfig(CfAnalysisConfig config) {
+ this.config = config;
+ return this;
+ }
+
+ public Builder setEventConsumer(CfFrameVerifierEventConsumer eventConsumer) {
+ this.eventConsumer = eventConsumer;
+ return this;
+ }
+
+ public CfFrameVerifier build() {
+ assert eventConsumer != null;
+ return new CfFrameVerifier(
+ appView,
+ code,
+ buildConfig(),
+ eventConsumer,
+ method,
+ previousMethod,
+ previousMethodIsInstance);
+ }
+
+ private CfAnalysisConfig buildConfig() {
+ return config != null
+ ? config
+ : new CfFrameVerifierDefaultAnalysisConfig(appView, code, method, previousMethod);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
new file mode 100644
index 0000000..82ea7ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
+import java.util.Optional;
+
+public class CfFrameVerifierDefaultAnalysisConfig implements CfAnalysisConfig {
+
+ private final CfAssignability assignability;
+ private final CfCode code;
+ private final ProgramMethod method;
+ private final Optional<DexMethod> previousMethod;
+
+ CfFrameVerifierDefaultAnalysisConfig(
+ AppView<?> appView, CfCode code, ProgramMethod method, Optional<DexMethod> previousMethod) {
+ this.assignability = new CfAssignability(appView);
+ this.code = code;
+ this.method = method;
+ this.previousMethod = previousMethod;
+ }
+
+ @Override
+ public CfAssignability getAssignability() {
+ return assignability;
+ }
+
+ @Override
+ public DexMethod getCurrentContext() {
+ return previousMethod.orElse(method.getReference());
+ }
+
+ @Override
+ public int getMaxLocals() {
+ return code.getMaxLocals();
+ }
+
+ @Override
+ public int getMaxStack() {
+ return code.getMaxStack();
+ }
+
+ @Override
+ public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
+ // If the code is rewritten according to the graph lens, we perform a strict check that the
+ // given type is the same as the current holder's super class.
+ if (!previousMethod.isPresent()) {
+ return type == method.getHolder().getSuperType();
+ }
+ // Otherwise, we don't know what the super class of the current class was at the point of the
+ // code lens. We return true, which has the consequence that we may accept a constructor call
+ // for an uninitialized-this value where the constructor is not defined in the immediate parent
+ // class.
+ return true;
+ }
+
+ @Override
+ public boolean isStrengthenFramesEnabled() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.java
new file mode 100644
index 0000000..043dea6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.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 com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.graph.CfCodeDiagnostics;
+import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
+
+public interface CfFrameVerifierEventConsumer {
+
+ default void acceptError(CfCodeDiagnostics diagnostics) {}
+
+ default void acceptInstructionState(CfInstruction instruction, CfFrameState state) {}
+}
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 089f5ee..5384772 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
@@ -43,6 +43,11 @@
}
@Override
+ public boolean isInstanceFieldGet() {
+ return true;
+ }
+
+ @Override
public CfFieldInstruction createWithField(DexField otherField) {
return new CfInstanceFieldRead(otherField);
}
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 c492db1..594f126 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
@@ -46,6 +46,21 @@
}
@Override
+ public boolean isFieldPut() {
+ return true;
+ }
+
+ @Override
+ public boolean isInstanceFieldPut() {
+ return true;
+ }
+
+ @Override
+ public CfInstanceFieldWrite asInstanceFieldPut() {
+ return this;
+ }
+
+ @Override
void internalRegisterUse(
UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
registry.registerInstanceFieldWrite(getField());
@@ -71,6 +86,7 @@
return frame
.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/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 66e3d69..bc8e8fb 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
@@ -135,6 +135,14 @@
return TraversalContinuation.doContinue(initialValue);
}
+ public boolean isArrayStore() {
+ return false;
+ }
+
+ public CfArrayStore asArrayStore() {
+ return null;
+ }
+
@Override
public CfInstruction asCfInstruction() {
return this;
@@ -182,6 +190,38 @@
return false;
}
+ public boolean isFieldGet() {
+ return false;
+ }
+
+ public boolean isInstanceFieldGet() {
+ return false;
+ }
+
+ public boolean isStaticFieldGet() {
+ return false;
+ }
+
+ public boolean isFieldPut() {
+ return false;
+ }
+
+ public boolean isInstanceFieldPut() {
+ return false;
+ }
+
+ public CfInstanceFieldWrite asInstanceFieldPut() {
+ return null;
+ }
+
+ public boolean isStaticFieldPut() {
+ return false;
+ }
+
+ public CfStaticFieldWrite asStaticFieldPut() {
+ return null;
+ }
+
public CfGoto asGoto() {
return null;
}
@@ -311,6 +351,10 @@
return false;
}
+ public CfReturn asReturn() {
+ return null;
+ }
+
public boolean isReturnVoid() {
return false;
}
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 fe5af15..8fb0024 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
@@ -106,6 +106,11 @@
}
@Override
+ public CfReturn asReturn() {
+ return this;
+ }
+
+ @Override
public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
Slot pop = state.pop();
builder.addReturn(pop.register);
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 bdad2a3..c904d10 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
@@ -42,11 +42,21 @@
}
@Override
+ public boolean isFieldPut() {
+ return true;
+ }
+
+ @Override
public boolean isStaticFieldPut() {
return true;
}
@Override
+ public CfStaticFieldWrite asStaticFieldPut() {
+ return this;
+ }
+
+ @Override
void internalRegisterUse(
UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
registry.registerStaticFieldWrite(getField());
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 e8eb7b3..42ba495 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
@@ -76,12 +76,17 @@
}
@Override
+ public NullFrameType asNullType() {
+ return null;
+ }
+
+ @Override
public boolean isObject() {
return false;
}
@Override
- public DexType getObjectType(DexType context) {
+ public DexType getObjectType(DexItemFactory dexItemFactory, DexType context) {
assert false : "Unexpected use of getObjectType() for non-object FrameType";
return null;
}
@@ -139,6 +144,38 @@
}
@Override
+ public boolean isInitializedNonNullReferenceType() {
+ return false;
+ }
+
+ @Override
+ public InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType() {
+ return null;
+ }
+
+ @Override
+ public boolean isInitializedNonNullReferenceTypeWithoutInterfaces() {
+ return false;
+ }
+
+ @Override
+ public InitializedNonNullReferenceFrameTypeWithoutInterfaces
+ asInitializedNonNullReferenceTypeWithoutInterfaces() {
+ return null;
+ }
+
+ @Override
+ public boolean isInitializedNonNullReferenceTypeWithInterfaces() {
+ return false;
+ }
+
+ @Override
+ public InitializedNonNullReferenceFrameTypeWithInterfaces
+ asInitializedNonNullReferenceTypeWithInterfaces() {
+ return null;
+ }
+
+ @Override
public boolean isWide() {
return false;
}
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 823380f..bf16f11 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
@@ -9,6 +9,9 @@
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.ir.analysis.type.PrimitiveTypeElement;
+import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.naming.NamingLens;
@@ -63,14 +66,50 @@
return initializedReference(type);
}
+ static InitializedFrameType initialized(TypeElement type) {
+ if (type.isPrimitiveType()) {
+ return primitive(type.asPrimitiveType());
+ }
+ return initializedReference(type.asReferenceType());
+ }
+
static InitializedReferenceFrameType initializedReference(DexType type) {
assert type.isReferenceType();
- return new InitializedReferenceFrameType(type);
+ return type.isNullValueType() ? nullType() : initializedNonNullReference(type);
+ }
+
+ static InitializedReferenceFrameType initializedReference(ReferenceTypeElement type) {
+ return type.isNullType() ? nullType() : initializedNonNullReference(type);
+ }
+
+ static InitializedNonNullReferenceFrameTypeWithoutInterfaces initializedNonNullReference(
+ DexType type) {
+ assert type.isReferenceType();
+ assert !type.isNullValueType();
+ return new InitializedNonNullReferenceFrameTypeWithoutInterfaces(type);
+ }
+
+ static InitializedNonNullReferenceFrameTypeWithInterfaces initializedNonNullReference(
+ ReferenceTypeElement type) {
+ assert !type.isNullType();
+ return new InitializedNonNullReferenceFrameTypeWithInterfaces(type);
+ }
+
+ static NullFrameType nullType() {
+ return NullFrameType.SINGLETON;
}
static PrimitiveFrameType primitive(DexType type) {
assert type.isPrimitiveType();
- switch (type.getDescriptor().getFirstByteAsChar()) {
+ return internalPrimitive(type.getDescriptor().getFirstByteAsChar());
+ }
+
+ static PrimitiveFrameType primitive(PrimitiveTypeElement type) {
+ return internalPrimitive(type.getDescriptor().charAt(0));
+ }
+
+ static PrimitiveFrameType internalPrimitive(char descriptor) {
+ switch (descriptor) {
case 'Z':
return booleanType();
case 'B':
@@ -88,7 +127,7 @@
case 'S':
return shortType();
default:
- throw new Unreachable("Unexpected primitive type: " + type.getTypeName());
+ throw new Unreachable("Unexpected primitive type: " + descriptor);
}
}
@@ -116,7 +155,7 @@
assert memberType.isPrecise();
switch (memberType) {
case OBJECT:
- return FrameType.initialized(factory.objectType);
+ return FrameType.initializedNonNullReference(factory.objectType);
case BOOLEAN_OR_BYTE:
case CHAR:
case SHORT:
@@ -135,7 +174,7 @@
DexType getInitializedType(DexItemFactory dexItemFactory);
- DexType getObjectType(DexType context);
+ DexType getObjectType(DexItemFactory dexItemFactory, DexType context);
Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens);
@@ -165,6 +204,20 @@
InitializedReferenceFrameType asInitializedReferenceType();
+ boolean isInitializedNonNullReferenceType();
+
+ InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType();
+
+ boolean isInitializedNonNullReferenceTypeWithoutInterfaces();
+
+ InitializedNonNullReferenceFrameTypeWithoutInterfaces
+ asInitializedNonNullReferenceTypeWithoutInterfaces();
+
+ boolean isInitializedNonNullReferenceTypeWithInterfaces();
+
+ InitializedNonNullReferenceFrameTypeWithInterfaces
+ asInitializedNonNullReferenceTypeWithInterfaces();
+
boolean isInt();
boolean isLong();
@@ -175,6 +228,8 @@
boolean isNullType();
+ NullFrameType asNullType();
+
boolean isObject();
boolean isOneWord();
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java
new file mode 100644
index 0000000..d55633a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameType.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf.code.frame;
+
+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.ir.analysis.type.ReferenceTypeElement;
+
+public abstract class InitializedNonNullReferenceFrameType extends BaseFrameType
+ implements InitializedReferenceFrameType {
+
+ @Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public final boolean isInitializedReferenceType() {
+ return true;
+ }
+
+ @Override
+ public final InitializedNonNullReferenceFrameType asInitializedReferenceType() {
+ return this;
+ }
+
+ @Override
+ public final boolean isInitializedNonNullReferenceType() {
+ return true;
+ }
+
+ @Override
+ public final InitializedNonNullReferenceFrameType asInitializedNonNullReferenceType() {
+ return this;
+ }
+
+ @Override
+ public final boolean isObject() {
+ return true;
+ }
+
+ @Override
+ public final boolean isPrecise() {
+ return true;
+ }
+
+ @Override
+ public final PreciseFrameType asPrecise() {
+ return this;
+ }
+
+ @Override
+ public final SingleFrameType asSingle() {
+ return this;
+ }
+
+ public abstract ReferenceTypeElement getInitializedTypeWithInterfaces(
+ AppView<? extends AppInfoWithClassHierarchy> appView);
+
+ @Override
+ public final DexType getObjectType(DexItemFactory dexItemFactory, DexType context) {
+ return getInitializedType(dexItemFactory);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithInterfaces.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithInterfaces.java
new file mode 100644
index 0000000..d1187c0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithInterfaces.java
@@ -0,0 +1,105 @@
+// 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.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.GraphLens;
+import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
+import com.android.tools.r8.naming.NamingLens;
+
+public class InitializedNonNullReferenceFrameTypeWithInterfaces
+ extends InitializedNonNullReferenceFrameType {
+
+ private final ReferenceTypeElement type;
+ private DexType initializedTypeCache;
+
+ InitializedNonNullReferenceFrameTypeWithInterfaces(ReferenceTypeElement type) {
+ assert type != null;
+ assert !type.isNullType();
+ this.type = type;
+ }
+
+ @Override
+ public boolean isInitializedNonNullReferenceTypeWithInterfaces() {
+ return true;
+ }
+
+ @Override
+ public InitializedNonNullReferenceFrameTypeWithInterfaces
+ asInitializedNonNullReferenceTypeWithInterfaces() {
+ return this;
+ }
+
+ @Override
+ public DexType getInitializedType(DexItemFactory dexItemFactory) {
+ if (initializedTypeCache == null) {
+ initializedTypeCache = type.toDexType(dexItemFactory);
+ }
+ return initializedTypeCache;
+ }
+
+ @Override
+ public ReferenceTypeElement getInitializedTypeWithInterfaces(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return getInitializedTypeWithInterfaces();
+ }
+
+ public ReferenceTypeElement getInitializedTypeWithInterfaces() {
+ return type;
+ }
+
+ @Override
+ public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ throw new Unreachable(
+ "Unexpected InitializedNonNullReferenceFrameTypeWithInterfaces in writer");
+ }
+
+ @Override
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
+ if (equals(frameType) || frameType.isNullType()) {
+ return this;
+ }
+ if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
+ return FrameType.oneWord();
+ }
+ assert frameType.isInitializedNonNullReferenceType();
+ ReferenceTypeElement joinType =
+ getInitializedTypeWithInterfaces(appView)
+ .join(
+ frameType
+ .asInitializedNonNullReferenceType()
+ .getInitializedTypeWithInterfaces(appView),
+ appView);
+ return FrameType.initializedNonNullReference(joinType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InitializedNonNullReferenceFrameTypeWithInterfaces initializedType =
+ (InitializedNonNullReferenceFrameTypeWithInterfaces) obj;
+ return type.equals(initializedType.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Initialized(" + type.toString() + ")";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithoutInterfaces.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithoutInterfaces.java
new file mode 100644
index 0000000..88beb14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedNonNullReferenceFrameTypeWithoutInterfaces.java
@@ -0,0 +1,117 @@
+// 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.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.GraphLens;
+import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.Opcodes;
+
+public class InitializedNonNullReferenceFrameTypeWithoutInterfaces
+ extends InitializedNonNullReferenceFrameType {
+
+ private final DexType type;
+
+ InitializedNonNullReferenceFrameTypeWithoutInterfaces(DexType type) {
+ assert type != null;
+ assert type.isReferenceType();
+ assert !type.isNullValueType();
+ this.type = type;
+ }
+
+ @Override
+ public boolean isInitializedNonNullReferenceTypeWithoutInterfaces() {
+ return true;
+ }
+
+ @Override
+ public InitializedNonNullReferenceFrameTypeWithoutInterfaces
+ asInitializedNonNullReferenceTypeWithoutInterfaces() {
+ return this;
+ }
+
+ @Override
+ public final DexType getInitializedType(DexItemFactory dexItemFactory) {
+ return getInitializedType();
+ }
+
+ public DexType getInitializedType() {
+ return type;
+ }
+
+ @Override
+ public ReferenceTypeElement getInitializedTypeWithInterfaces(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return type.toTypeElement(appView).asReferenceType();
+ }
+
+ @Override
+ public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ DexType rewrittenType = graphLens.lookupType(type);
+ assert rewrittenType != DexItemFactory.nullValueType;
+ switch (rewrittenType.toShorty()) {
+ case 'L':
+ return namingLens.lookupInternalName(rewrittenType);
+ case 'I':
+ return Opcodes.INTEGER;
+ case 'F':
+ return Opcodes.FLOAT;
+ case 'J':
+ return Opcodes.LONG;
+ case 'D':
+ return Opcodes.DOUBLE;
+ default:
+ throw new Unreachable("Unexpected value type: " + rewrittenType);
+ }
+ }
+
+ @Override
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
+ if (equals(frameType) || frameType.isNullType()) {
+ return this;
+ }
+ if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
+ return FrameType.oneWord();
+ }
+ assert frameType.isInitializedNonNullReferenceType();
+ ReferenceTypeElement joinType =
+ getInitializedTypeWithInterfaces(appView)
+ .join(
+ frameType
+ .asInitializedNonNullReferenceType()
+ .getInitializedTypeWithInterfaces(appView),
+ appView);
+ return FrameType.initializedNonNullReference(joinType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ InitializedNonNullReferenceFrameTypeWithoutInterfaces initializedType =
+ (InitializedNonNullReferenceFrameTypeWithoutInterfaces) obj;
+ return type == initializedType.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Initialized(" + type.toString() + ")";
+ }
+}
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 27ada3e..4094f4e 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,154 +4,4 @@
package com.android.tools.r8.cf.code.frame;
-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;
-
-public class InitializedReferenceFrameType extends BaseFrameType
- implements InitializedFrameType, SingleFrameType {
-
- private final DexType type;
-
- public InitializedReferenceFrameType(DexType type) {
- assert type != null;
- assert type.isReferenceType();
- this.type = type;
- }
-
- @Override
- public boolean isPrecise() {
- return true;
- }
-
- @Override
- public PreciseFrameType asPrecise() {
- return this;
- }
-
- @Override
- public boolean isInitializedReferenceType() {
- return true;
- }
-
- @Override
- public InitializedReferenceFrameType asInitializedReferenceType() {
- return this;
- }
-
- @Override
- public SingleFrameType join(
- AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
- if (equals(frameType)) {
- return this;
- }
- if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
- return FrameType.oneWord();
- }
- DexType otherType = frameType.asInitializedReferenceType().getInitializedType();
- assert type != otherType;
- assert type.isReferenceType();
- if (isNullType()) {
- return otherType.isReferenceType() ? frameType : FrameType.oneWord();
- }
- if (frameType.isNullType()) {
- return this;
- }
- assert type.isArrayType() || type.isClassType();
- assert otherType.isArrayType() || otherType.isClassType();
- DexType joinType =
- DexTypeUtils.toDexType(
- appView, type.toTypeElement(appView).join(otherType.toTypeElement(appView), appView));
- return FrameType.initializedReference(joinType);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- InitializedReferenceFrameType initializedType = (InitializedReferenceFrameType) obj;
- return type == initializedType.type;
- }
-
- @Override
- public int hashCode() {
- return type.hashCode();
- }
-
- @Override
- public String toString() {
- return "Initialized(" + type.toString() + ")";
- }
-
- @Override
- public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
- DexType rewrittenType = graphLens.lookupType(type);
- if (rewrittenType == DexItemFactory.nullValueType) {
- return Opcodes.NULL;
- }
- switch (rewrittenType.toShorty()) {
- case 'L':
- return namingLens.lookupInternalName(rewrittenType);
- case 'I':
- return Opcodes.INTEGER;
- case 'F':
- return Opcodes.FLOAT;
- case 'J':
- return Opcodes.LONG;
- case 'D':
- return Opcodes.DOUBLE;
- default:
- throw new Unreachable("Unexpected value type: " + rewrittenType);
- }
- }
-
- @Override
- public SingleFrameType asSingle() {
- return this;
- }
-
- @Override
- public boolean isWide() {
- return false;
- }
-
- @Override
- public boolean isInitialized() {
- return true;
- }
-
- public DexType getInitializedType() {
- return type;
- }
-
- @Override
- public DexType getInitializedType(DexItemFactory dexItemFactory) {
- return getInitializedType();
- }
-
- @Override
- public boolean isNullType() {
- return type.isNullValueType();
- }
-
- @Override
- public boolean isObject() {
- return type.isReferenceType();
- }
-
- @Override
- public DexType getObjectType(DexType context) {
- assert isObject() : "Unexpected use of getObjectType() for non-object FrameType";
- return type;
- }
-}
+public interface InitializedReferenceFrameType extends InitializedFrameType, SingleFrameType {}
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.java
new file mode 100644
index 0000000..a5e26c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/NullFrameType.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.cf.code.frame;
+
+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.GraphLens;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.Opcodes;
+
+public class NullFrameType extends SingletonFrameType implements InitializedReferenceFrameType {
+
+ static final NullFrameType SINGLETON = new NullFrameType();
+
+ private NullFrameType() {}
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public boolean isInitializedReferenceType() {
+ return true;
+ }
+
+ @Override
+ public NullFrameType asInitializedReferenceType() {
+ return this;
+ }
+
+ @Override
+ public boolean isNullType() {
+ return true;
+ }
+
+ @Override
+ public NullFrameType asNullType() {
+ return this;
+ }
+
+ @Override
+ public boolean isObject() {
+ return true;
+ }
+
+ @Override
+ public boolean isPrecise() {
+ return true;
+ }
+
+ @Override
+ public PreciseFrameType asPrecise() {
+ return this;
+ }
+
+ @Override
+ public SingleFrameType asSingle() {
+ return this;
+ }
+
+ @Override
+ public DexType getInitializedType(DexItemFactory dexItemFactory) {
+ return getInitializedType();
+ }
+
+ public DexType getInitializedType() {
+ return DexItemFactory.nullValueType;
+ }
+
+ @Override
+ public DexType getObjectType(DexItemFactory dexItemFactory, DexType context) {
+ return getInitializedType();
+ }
+
+ @Override
+ public Object getTypeOpcode(GraphLens graphLens, NamingLens namingLens) {
+ return Opcodes.NULL;
+ }
+
+ @Override
+ public SingleFrameType join(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
+ if (this == frameType) {
+ return this;
+ }
+ if (frameType.isOneWord() || frameType.isPrimitive() || frameType.isUninitialized()) {
+ return FrameType.oneWord();
+ }
+ assert frameType.isInitializedNonNullReferenceType();
+ return frameType;
+ }
+
+ @Override
+ public String toString() {
+ return "null";
+ }
+}
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 dfa0786..11f9a33 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
@@ -11,20 +11,18 @@
@Override
default PreciseFrameType map(Function<DexType, DexType> fn) {
- if (isObject()) {
- if (isInitialized()) {
- DexType type = asInitializedReferenceType().getInitializedType();
- DexType newType = fn.apply(type);
- if (type != newType) {
- return FrameType.initializedReference(newType);
- }
+ assert !isInitializedNonNullReferenceTypeWithInterfaces();
+ if (isInitializedNonNullReferenceTypeWithoutInterfaces()) {
+ DexType type = asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType();
+ DexType newType = fn.apply(type);
+ if (type != newType) {
+ return FrameType.initializedNonNullReference(newType);
}
- if (isUninitializedNew()) {
- DexType type = getUninitializedNewType();
- DexType newType = fn.apply(type);
- if (type != newType) {
- return FrameType.uninitializedNew(getUninitializedLabel(), newType);
- }
+ } else if (isUninitializedNew()) {
+ DexType type = getUninitializedNewType();
+ DexType newType = fn.apply(type);
+ if (type != newType) {
+ return FrameType.uninitializedNew(getUninitializedLabel(), newType);
}
}
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 a35e4b4..a57c5e6 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
@@ -7,6 +7,7 @@
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.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.naming.NamingLens;
@@ -24,7 +25,7 @@
}
@Override
- public DexType getObjectType(DexType context) {
+ public DexType getObjectType(DexItemFactory dexItemFactory, DexType context) {
return type;
}
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 5b6730d..21036e4 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
@@ -6,6 +6,7 @@
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.GraphLens;
import com.android.tools.r8.naming.NamingLens;
@@ -18,7 +19,7 @@
private UninitializedThis() {}
@Override
- public DexType getObjectType(DexType context) {
+ public DexType getObjectType(DexItemFactory dexItemFactory, DexType context) {
return context;
}
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 44289af..0d4f9c9 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -9,7 +9,9 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfFrame;
-import com.android.tools.r8.cf.code.CfFrameVerificationHelper;
+import com.android.tools.r8.cf.code.CfFrameVerifier;
+import com.android.tools.r8.cf.code.CfFrameVerifier.StackMapStatus;
+import com.android.tools.r8.cf.code.CfFrameVerifierEventConsumer;
import com.android.tools.r8.cf.code.CfIinc;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
@@ -17,7 +19,6 @@
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.frame.FrameType;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.dex.code.DexBase5Format;
import com.android.tools.r8.errors.InvalidDebugInfoException;
@@ -39,47 +40,29 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
-import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
+import java.util.Set;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
public class CfCode extends Code implements CfWritableCode, StructuralItem<CfCode> {
- public enum StackMapStatus {
- NOT_VERIFIED,
- NOT_PRESENT,
- INVALID,
- VALID;
-
- public boolean isValid() {
- return this == VALID || this == NOT_PRESENT;
- }
-
- public boolean isInvalidOrNotPresent() {
- return this == INVALID || this == NOT_PRESENT;
- }
- }
-
public static class LocalVariableInfo {
private final int index;
@@ -149,7 +132,7 @@
private List<CfInstruction> instructions;
private final List<CfTryCatch> tryCatchRanges;
private final List<LocalVariableInfo> localVariables;
- private StackMapStatus stackMapStatus = StackMapStatus.NOT_VERIFIED;
+ private StackMapStatus stackMapStatus = CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
private final com.android.tools.r8.position.Position diagnosticPosition;
private final BytecodeMetadata<CfInstruction> metadata;
@@ -261,7 +244,7 @@
}
public StackMapStatus getStackMapStatus() {
- assert stackMapStatus != StackMapStatus.NOT_VERIFIED;
+ assert stackMapStatus != CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
return stackMapStatus;
}
@@ -281,6 +264,15 @@
return tryCatchRanges;
}
+ public Set<CfLabel> getTryCatchRangeLabels() {
+ Set<CfLabel> tryCatchRangeLabels = Sets.newIdentityHashSet();
+ for (CfTryCatch tryCatchRange : getTryCatchRanges()) {
+ tryCatchRangeLabels.add(tryCatchRange.start);
+ tryCatchRangeLabels.add(tryCatchRange.end);
+ }
+ return tryCatchRangeLabels;
+ }
+
public CfInstruction getInstruction(int index) {
return instructions.get(index);
}
@@ -415,7 +407,8 @@
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
GraphLens graphLens = appView.graphLens();
- assert verifyFrames(method, appView).isValid() : "Could not validate stack map frames";
+ assert verifyFrames(method, appView).isValidOrNotPresent()
+ : "Could not validate stack map frames";
DexItemFactory dexItemFactory = appView.dexItemFactory();
InitClassLens initClassLens = appView.initClassLens();
InternalOptions options = appView.options();
@@ -553,7 +546,7 @@
private void verifyFramesOrRemove(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
stackMapStatus = verifyFrames(method, appView, codeLens);
- if (!stackMapStatus.isValid()) {
+ if (!stackMapStatus.isValidOrNotPresent()) {
ArrayList<CfInstruction> copy = new ArrayList<>(instructions);
copy.removeIf(CfInstruction::isFrame);
setInstructions(copy);
@@ -896,184 +889,23 @@
}
public StackMapStatus verifyFrames(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
- GraphLens graphLens = appView.graphLens();
- DexEncodedMethod definition = method.getDefinition();
- if (!appView.options().canUseInputStackMaps()
- || appView.options().testing.disableStackMapVerification) {
- return StackMapStatus.NOT_PRESENT;
- }
- if (definition.hasClassFileVersion()
- && definition.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
- return StackMapStatus.NOT_PRESENT;
- }
+ CfFrameVerifierEventConsumer eventConsumer =
+ new CfFrameVerifierEventConsumer() {
- RewrittenPrototypeDescription protoChanges =
- graphLens.lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens);
-
- DexMethod previousMethodSignature =
- graphLens.getOriginalMethodSignature(method.getReference(), codeLens);
- boolean previousMethodSignatureIsInstance =
- method.getDefinition().isInstance()
- || protoChanges.getArgumentInfoCollection().isConvertedToStaticMethod();
-
- // Build a map from labels to frames.
- Map<CfLabel, CfFrame> stateMap = new IdentityHashMap<>();
- List<CfLabel> labels = new ArrayList<>();
- boolean requireStackMapFrame = !tryCatchRanges.isEmpty();
- for (CfInstruction instruction : instructions) {
- if (instruction.isFrame()) {
- CfFrame frame = instruction.asFrame();
- if (!labels.isEmpty()) {
- for (CfLabel label : labels) {
- if (stateMap.containsKey(label)) {
- return reportStackMapError(
- CfCodeStackMapValidatingException.multipleFramesForLabel(method, appView),
- appView);
- }
- stateMap.put(label, frame);
+ @Override
+ public void acceptError(CfCodeDiagnostics diagnostics) {
+ // Stack maps was required from version V1_6 (50), but the JVM gave a grace-period and
+ // only started enforcing stack maps from 51 in JVM 8. As a consequence, we have
+ // different android libraries that has V1_7 code but has no stack maps. To not fail on
+ // compilations we only report a warning.
+ appView.options().reporter.warning(diagnostics);
}
- } else if (instruction != instructions.get(0)) {
- // From b/168212806, it is possible that the first instruction is a frame.
- return reportStackMapError(
- CfCodeStackMapValidatingException.unexpectedStackMapFrame(method, appView), appView);
- }
- }
- // We are trying to map a frame to a label, but we can have positions in between, so skip
- // those.
- if (instruction.isPosition()) {
- continue;
- } else if (instruction.isLabel()) {
- labels.add(instruction.asLabel());
- } else {
- labels.clear();
- }
- if (!requireStackMapFrame) {
- requireStackMapFrame = instruction.isJump() && !finalAndExitInstruction(instruction);
- }
- }
- // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
- if (requireStackMapFrame && stateMap.isEmpty()) {
- return reportStackMapError(
- CfCodeStackMapValidatingException.noFramesForMethodWithJumps(method, appView), appView);
- }
- CfFrameVerificationHelper helper =
- new CfFrameVerificationHelper(appView, this, codeLens, method, stateMap, tryCatchRanges);
- CfCodeDiagnostics diagnostics = helper.checkTryCatchRanges();
- if (diagnostics != null) {
- return reportStackMapError(diagnostics, appView);
- }
- TraversalContinuation<CfCodeDiagnostics, CfFrameState> initialState =
- computeInitialState(
- appView, helper, method, previousMethodSignature, previousMethodSignatureIsInstance);
- if (initialState.shouldBreak()) {
- return reportStackMapError(initialState.asBreak().getValue(), appView);
- }
- CfFrameState state = initialState.asContinue().getValue();
- for (int i = 0; i < instructions.size(); i++) {
- CfInstruction instruction = instructions.get(i);
- assert !state.isError();
- // Check the exceptional edge prior to evaluating the instruction. The local state is stable
- // at this point as store operations are not throwing and the current stack does not
- // affect the exceptional transfer (the exception edge is always a singleton stack).
- if (instruction.canThrow()) {
- assert !instruction.isStore();
- state = helper.checkExceptionEdges(state);
- }
- if (instruction.isLabel()) {
- helper.seenLabel(instruction.asLabel());
- }
- state = instruction.evaluate(state, appView, helper);
- if (instruction.isJumpWithNormalTarget()) {
- CfInstruction fallthroughInstruction =
- (i + 1) < instructions.size() ? instructions.get(i + 1) : null;
- TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
- instruction.traverseNormalTargets(
- (target, currentState) -> {
- if (target != fallthroughInstruction) {
- assert target.isLabel();
- currentState = helper.checkTarget(currentState, target.asLabel());
- }
- return TraversalContinuation.doContinue(currentState);
- },
- fallthroughInstruction,
- state);
- state = traversalContinuation.asContinue().getValue();
- }
- TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
- helper.computeStateForNextInstruction(instruction, i, state);
- if (traversalContinuation.isContinue()) {
- state = traversalContinuation.asContinue().getValue();
- } else {
- return reportStackMapError(traversalContinuation.asBreak().getValue(), appView);
- }
- if (state.isError()) {
- return reportStackMapError(
- CfCodeStackMapValidatingException.invalidStackMapForInstruction(
- method, i, instruction, state.asError().getMessage(), appView),
- appView);
- }
- }
- return StackMapStatus.VALID;
- }
-
- private StackMapStatus reportStackMapError(CfCodeDiagnostics diagnostics, AppView<?> appView) {
- // Stack maps was required from version V1_6 (50), but the JVM gave a grace-period and only
- // started enforcing stack maps from 51 in JVM 8. As a consequence, we have different android
- // libraries that has V1_7 code but has no stack maps. To not fail on compilations we only
- // report a warning.
- appView.options().reporter.warning(diagnostics);
- return StackMapStatus.INVALID;
- }
-
- private boolean finalAndExitInstruction(CfInstruction instruction) {
- boolean isReturnOrThrow = instruction.isThrow() || instruction.isReturn();
- if (!isReturnOrThrow) {
- return false;
- }
- for (int i = instructions.size() - 1; i >= 0; i--) {
- CfInstruction instr = instructions.get(i);
- if (instr == instruction) {
- return true;
- }
- if (instr.isPosition() || instr.isLabel()) {
- continue;
- }
- return false;
- }
- throw new Unreachable("Instruction " + instruction + " should be in instructions");
- }
-
- private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeInitialState(
- AppView<?> appView,
- CfFrameVerificationHelper helper,
- ProgramMethod method,
- DexMethod previousMethodSignature,
- boolean previousMethodSignatureIsInstance) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- CfFrameState state = new ConcreteCfFrameState();
- int localIndex = 0;
- if (previousMethodSignatureIsInstance) {
- state =
- state.storeLocal(
- localIndex,
- previousMethodSignature.isInstanceInitializer(dexItemFactory)
- || previousMethodSignature.mustBeInlinedIntoInstanceInitializer(appView)
- || previousMethodSignature.isHorizontallyMergedInstanceInitializer(
- dexItemFactory)
- ? FrameType.uninitializedThis()
- : FrameType.initialized(previousMethodSignature.getHolderType()),
- helper);
- localIndex++;
- }
- for (DexType parameter : previousMethodSignature.getParameters()) {
- state = state.storeLocal(localIndex, FrameType.initialized(parameter), helper);
- localIndex += parameter.getRequiredRegisters();
- }
- if (state.isError()) {
- return TraversalContinuation.doBreak(
- CfCodeStackMapValidatingException.invalidStackMapForInstruction(
- method, 0, instructions.get(0), state.asError().getMessage(), appView));
- }
- return TraversalContinuation.doContinue(state);
+ };
+ CfFrameVerifier helper =
+ CfFrameVerifier.builder(appView, this, method)
+ .setCodeLens(codeLens)
+ .setEventConsumer(eventConsumer)
+ .build();
+ return helper.run();
}
}
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 5c8046f..fbd4b5a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2127,6 +2127,10 @@
return appendPrimitiveMethods.contains(method);
}
+ public boolean isAppendSubArrayMethod(DexMethod method) {
+ return appendSubCharArray == method || appendSubCharSequence == method;
+ }
+
public boolean isAppendStringMethod(DexMethod method) {
return method == appendString;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 1efc762..36c45bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -157,8 +157,7 @@
if (clazz == null) {
return false;
}
- // TODO(b/214496607): Allow uninstantiated reasoning for closed interfaces.
- if (clazz.isInterface()) {
+ if (clazz.isInterface() && appView.getOpenClosedInterfacesCollection().isMaybeOpen(clazz)) {
return false;
}
return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index 549bbf2..a2acb83 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -33,6 +33,7 @@
this.variants = variants;
}
+ @Override
public DexType toDexType(DexItemFactory factory) {
TypeElement baseTypeLattice = getBaseType();
DexType baseType;
@@ -183,22 +184,23 @@
// Return null indicating the join is the same as the member to avoid object allocation.
return null;
}
- if (aMember.isArrayType() && bMember.isArrayType()) {
- TypeElement aMemberMember = aMember.asArrayType().getMemberType();
- TypeElement bMemberMember = bMember.asArrayType().getMemberType();
- TypeElement join =
- joinMember(
- aMemberMember,
- bMemberMember,
- appView,
- aMember.nullability().join(bMember.nullability()));
- return join == null ? null : ArrayTypeElement.create(join, nullability);
- }
- if (aMember.isClassType() && bMember.isClassType()) {
- ReferenceTypeElement join = aMember.asClassType().join(bMember.asClassType(), appView);
+ if (aMember.isReferenceType() && bMember.isReferenceType()) {
+ if (aMember.isArrayType() && bMember.isArrayType()) {
+ TypeElement aMemberMember = aMember.asArrayType().getMemberType();
+ TypeElement bMemberMember = bMember.asArrayType().getMemberType();
+ TypeElement join =
+ joinMember(
+ aMemberMember,
+ bMemberMember,
+ appView,
+ aMember.nullability().join(bMember.nullability()));
+ return join == null ? null : ArrayTypeElement.create(join, nullability);
+ }
+ ReferenceTypeElement join =
+ aMember.asReferenceType().join(bMember.asReferenceType(), appView);
return ArrayTypeElement.create(join, nullability);
- }
- if (aMember.isPrimitiveType() || bMember.isPrimitiveType()) {
+ } else {
+ assert aMember.isPrimitiveType() || bMember.isPrimitiveType();
if (appView.enableWholeProgramOptimizations()) {
assert appView.hasClassHierarchy();
DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -215,6 +217,5 @@
}
return objectClassType(appView, nullability);
}
- return objectArrayType(appView, nullability);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
index e00ecf9..8c14b52 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/BooleanTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "Z";
+ }
+
+ @Override
public String getTypeName() {
return "boolean";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
index 849f508..b4b1179 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ByteTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "B";
+ }
+
+ @Override
public String getTypeName() {
return "byte";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
index 5a47cda..e7984bd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/CharTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "C";
+ }
+
+ @Override
public String getTypeName() {
return "char";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 3298b8e..6101905 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -153,6 +153,7 @@
return getOrCreateVariant(nullability().meet(nullability));
}
+ @Override
public DexType toDexType(DexItemFactory dexItemFactory) {
if (type == dexItemFactory.objectType) {
DexType singleKnownInterface = getInterfaces().getSingleKnownInterface();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
index 85a501c..430897a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DoubleTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "D";
+ }
+
+ @Override
public String getTypeName() {
return "double";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
index 9efadf8..d4463cc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/FloatTypeElement.java
@@ -11,6 +11,11 @@
}
@Override
+ public String getDescriptor() {
+ return "F";
+ }
+
+ @Override
public String getTypeName() {
return "float";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
index 881cb19..3ecd81b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/IntTypeElement.java
@@ -11,6 +11,11 @@
}
@Override
+ public String getDescriptor() {
+ return "I";
+ }
+
+ @Override
public String getTypeName() {
return "int";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
index 7f71468..b90dbb4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/LongTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "J";
+ }
+
+ @Override
public String getTypeName() {
return "long";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
index 9797274..1e5260b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeElement.java
@@ -12,6 +12,8 @@
/** A {@link TypeElement} that abstracts primitive types. */
public abstract class PrimitiveTypeElement extends TypeElement {
+ public abstract String getDescriptor();
+
public abstract String getTypeName();
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
index 4aa2116..1f06630 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
public abstract class ReferenceTypeElement extends TypeElement {
@@ -39,6 +40,11 @@
}
@Override
+ public DexType toDexType(DexItemFactory dexItemFactory) {
+ return DexItemFactory.nullValueType;
+ }
+
+ @Override
public String toString() {
return nullability.toString() + " " + DexItemFactory.nullValueType.toString();
}
@@ -116,6 +122,8 @@
return this;
}
+ public abstract DexType toDexType(DexItemFactory dexItemFactory);
+
@Override
public boolean equals(Object o) {
throw new Unreachable("Should be implemented on each sub type");
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
index fd7d65a..2c73f91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ShortTypeElement.java
@@ -12,6 +12,11 @@
}
@Override
+ public String getDescriptor() {
+ return "S";
+ }
+
+ @Override
public String getTypeName() {
return "short";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
index 5d3d16b..8814afc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/SinglePrimitiveTypeElement.java
@@ -19,6 +19,11 @@
}
@Override
+ public String getDescriptor() {
+ throw new Unreachable("Unexpected attempt to get descriptor of " + this);
+ }
+
+ @Override
public String getTypeName() {
throw new Unreachable("Unexpected attempt to get type name of " + this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
index f13800d..2180be1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/WidePrimitiveTypeElement.java
@@ -19,6 +19,11 @@
}
@Override
+ public String getDescriptor() {
+ throw new Unreachable("Unexpected attempt to get descriptor of " + this);
+ }
+
+ @Override
public String getTypeName() {
throw new Unreachable("Unexpected attempt to get type name of " + this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MemberType.java b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
index c85a687..a23e5b1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MemberType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
@@ -20,6 +20,10 @@
INT_OR_FLOAT,
LONG_OR_DOUBLE;
+ public boolean isObject() {
+ return this == OBJECT;
+ }
+
public boolean isPrecise() {
return this != INT_OR_FLOAT && this != LONG_OR_DOUBLE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index e6228f7..cb3bf73 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -193,7 +193,7 @@
DexBuilder.removeRedundantDebugPositions(code);
CfCode code = buildCfCode();
assert verifyInvokeInterface(code, appView);
- assert code.verifyFrames(method, appView, appView.graphLens()).isValid();
+ assert code.verifyFrames(method, appView, appView.graphLens()).isValidOrNotPresent();
return code;
}
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 4f28ee9..bffd1fe 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
@@ -6,6 +6,7 @@
import static it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMaps.emptyMap;
import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrameVerifier;
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLabel;
@@ -21,7 +22,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
-import com.android.tools.r8.graph.CfCode.StackMapStatus;
import com.android.tools.r8.graph.CfCodeDiagnostics;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
@@ -638,7 +638,12 @@
private DexType convertUninitialized(FrameType type) {
if (type.isInitializedReferenceType()) {
- return type.asInitializedReferenceType().getInitializedType();
+ if (type.isNullType()) {
+ return type.asNullType().getInitializedType();
+ } else {
+ assert type.isInitializedNonNullReferenceTypeWithoutInterfaces();
+ return type.asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType();
+ }
}
if (type.isPrimitive()) {
if (type.isSinglePrimitive()) {
@@ -688,7 +693,7 @@
@Override
public DexType getPhiTypeForBlock(
int register, int blockOffset, ValueTypeConstraint constraint, RegisterReadType readType) {
- assert code.getStackMapStatus() != StackMapStatus.NOT_VERIFIED;
+ assert code.getStackMapStatus() != CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
if (code.getStackMapStatus().isInvalidOrNotPresent()) {
return null;
}
@@ -876,7 +881,7 @@
@Override
public boolean hasValidTypesFromStackMap() {
- return code.getStackMapStatus() == StackMapStatus.VALID;
+ return code.getStackMapStatus() == CfFrameVerifier.StackMapStatus.VALID;
}
@Override
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 e504e3d..2a97c16 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
@@ -80,13 +80,13 @@
import com.android.tools.r8.ir.optimize.membervaluepropagation.MemberValuePropagation;
import com.android.tools.r8.ir.optimize.membervaluepropagation.R8MemberValuePropagation;
import com.android.tools.r8.ir.optimize.outliner.Outliner;
+import com.android.tools.r8.ir.optimize.string.StringBuilderAppendOptimizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
-import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysis;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
@@ -142,7 +142,6 @@
private final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
- private final OpenClosedInterfacesAnalysis openClosedInterfacesAnalysis;
public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -228,7 +227,6 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
- this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
this.assumeInserter = null;
return;
}
@@ -262,8 +260,6 @@
this.memberValuePropagation = new R8MemberValuePropagation(appViewWithLiveness);
this.methodOptimizationInfoCollector =
new MethodOptimizationInfoCollector(appViewWithLiveness, this);
- // TODO(b/214496607): Enable open/closed interfaces analysis.
- this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
if (options.isMinifying()) {
this.identifierNameStringMarker = new IdentifierNameStringMarker(appViewWithLiveness);
} else {
@@ -299,7 +295,6 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
- this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
}
this.stringSwitchRemover =
options.isStringSwitchConversionEnabled()
@@ -669,7 +664,6 @@
appView.withArgumentPropagator(
argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
- openClosedInterfacesAnalysis.prepareForPrimaryOptimizationPass();
outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
if (fieldAccessAnalysis != null) {
@@ -857,7 +851,6 @@
if (inliner != null) {
inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
}
- openClosedInterfacesAnalysis.onPrimaryOptimizationPassComplete();
// Ensure determinism of method-to-reprocess set.
appView.testing().checkDeterminism(postMethodProcessorBuilder::dump);
@@ -1164,8 +1157,6 @@
assert code.verifyTypes(appView);
assert code.isConsistentSSA(appView);
- openClosedInterfacesAnalysis.analyze(context, code);
-
if (shouldPassThrough(context)) {
// If the code is pass trough, do not finalize by overwriting the existing code.
assert appView.enableWholeProgramOptimizations();
@@ -1325,15 +1316,14 @@
timing.begin("Rewrite move result");
codeRewriter.rewriteMoveResult(code);
timing.end();
- // TODO(b/114002137): for now, string concatenation depends on rewriteMoveResult.
+ // TODO(b/114002137): Also run for CF
if (options.enableStringConcatenationOptimization
&& !isDebugMode
&& options.isGeneratingDex()) {
timing.begin("Rewrite string concat");
- stringBuilderOptimizer.computeTrivialStringConcatenation(code);
+ StringBuilderAppendOptimizer.run(appView, code);
timing.end();
}
-
timing.begin("Split range invokes");
codeRewriter.splitRangeInvokeConstants(code);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index ac5addd..fb51c23 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -685,16 +685,11 @@
return newMethod;
});
if (replacement != null) {
- // Since we've copied the code object from an existing method, the code should already be
- // processed, and thus we don't need to schedule it for processing in D8.
+ // Since we've copied the code object from an existing method from the same class, the
+ // code is already processed, and thus we don't need to schedule it for processing in D8.
assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode();
assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode();
- ProgramMethod newMethod = new ProgramMethod(implMethodHolder, replacement);
- if (appView.options().isDesugaredLibraryCompilation()) {
- assert appView.options().isGeneratingClassFiles();
- needsProcessingConsumer.accept(newMethod);
- }
- return newMethod;
+ return new ProgramMethod(implMethodHolder, replacement);
}
// The method might already have been moved by another invoke-dynamic targeting it.
// If so, it must be defined on the holder.
@@ -773,16 +768,11 @@
return newMethod;
});
if (replacement != null) {
- // Since we've copied the code object from an existing method, the code should already be
- // processed, and thus we don't need to schedule it for processing in D8.
+ // Since we've copied the code object from an existing method from the same class, the
+ // code is already processed, and thus we don't need to schedule it for processing in D8.
assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode();
assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode();
- ProgramMethod newMethod = new ProgramMethod(implMethodHolder, replacement);
- if (appView.options().isDesugaredLibraryCompilation()) {
- assert appView.options().isGeneratingClassFiles();
- needsProcessingConsumer.accept(newMethod);
- }
- return newMethod;
+ return new ProgramMethod(implMethodHolder, replacement);
}
// The method might already have been moved by another invoke-dynamic targeting it.
// If so, it must be defined on the holder.
@@ -846,10 +836,7 @@
.disableAndroidApiLevelCheck()
.build());
accessorClass.addDirectMethod(accessorMethod.getDefinition());
- if (appView.options().isDesugaredLibraryCompilation()
- || appView.options().isGeneratingDex()) {
- needsProcessingConsumer.accept(accessorMethod);
- }
+ needsProcessingConsumer.accept(accessorMethod);
return accessorMethod;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
index 0aa439a..83730de 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -87,7 +87,9 @@
CfLabel tryCatchHandler = new CfLabel();
builder.add(
tryCatchHandler,
- CfFrame.builder().push(FrameType.initialized(factory.throwableType)).build(),
+ CfFrame.builder()
+ .push(FrameType.initializedNonNullReference(factory.throwableType))
+ .build(),
new CfStore(ValueType.OBJECT, 0),
new CfNew(factory.serviceLoaderConfigurationErrorType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
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 73aeb93..99b113d 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
@@ -135,12 +135,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReferenceArray;")),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
@@ -167,12 +167,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReferenceArray;")),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
@@ -212,12 +212,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -244,12 +244,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -289,11 +289,11 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReference;")),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -317,11 +317,11 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/util/concurrent/atomic/AtomicReference;")),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
@@ -540,8 +540,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType()
})),
@@ -566,8 +566,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -614,8 +614,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -628,8 +628,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType()
})),
@@ -744,8 +744,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -792,12 +792,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 2),
label6,
@@ -861,11 +861,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
+ Arrays.asList(
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)))),
new CfStore(ValueType.OBJECT, 2),
label8,
new CfNew(options.itemFactory.createType("Ljava/lang/AssertionError;")),
@@ -928,12 +929,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType(
"Ljava/lang/reflect/InvocationTargetException;"))))),
new CfStore(ValueType.OBJECT, 2),
@@ -952,8 +953,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfGoto(label20),
label12,
@@ -961,11 +962,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
+ Arrays.asList(
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)))),
new CfStore(ValueType.OBJECT, 2),
label13,
new CfLoad(ValueType.OBJECT, 0),
@@ -1017,13 +1019,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.throwableType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
}),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 3),
label18,
@@ -1031,9 +1033,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.throwableType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfThrow(),
@@ -1042,9 +1044,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.throwableType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
})),
new CfLoad(ValueType.OBJECT, 2),
new CfThrow(),
@@ -1053,8 +1055,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfReturnVoid(),
label21),
@@ -1148,10 +1150,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/ArrayList;")),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
FrameType.intType(),
FrameType.intType()
})),
@@ -1190,8 +1194,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/ArrayList;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -1304,10 +1310,11 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashMap;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
FrameType.intType(),
FrameType.intType()
@@ -1421,10 +1428,11 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashMap;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
FrameType.intType(),
FrameType.intType()
@@ -1436,9 +1444,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/util/Map$Entry;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashMap;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -1497,9 +1506,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashSet;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
FrameType.intType(),
FrameType.intType()
})),
@@ -1581,9 +1593,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashSet;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
FrameType.intType(),
FrameType.intType()
})),
@@ -1594,8 +1609,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashSet;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -1662,11 +1679,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Collection;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/ArrayList;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Iterator;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -1713,9 +1731,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Collection;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/ArrayList;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/ArrayList;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -1793,9 +1812,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Map;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Map;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashMap;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Iterator;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -1871,8 +1893,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Map;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashMap;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Map;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashMap;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -1939,10 +1963,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Collection;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashSet;")),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Iterator;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -1989,9 +2015,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Collection;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/HashSet;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/HashSet;"))
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInvoke(
@@ -2455,7 +2482,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -2625,7 +2652,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType), FrameType.intType()
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.INT, 1),
@@ -2726,7 +2754,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.longType(),
FrameType.longHighType()
@@ -3194,7 +3222,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -3318,7 +3346,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3335,7 +3363,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3375,7 +3403,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3420,7 +3448,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3434,7 +3462,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3455,7 +3483,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3522,7 +3550,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3569,7 +3597,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3615,7 +3643,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -3644,7 +3672,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType(),
@@ -4033,7 +4061,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -4077,7 +4105,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 2),
@@ -4104,7 +4132,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType()
})),
new CfLoad(ValueType.LONG, 0),
@@ -4129,7 +4157,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType(),
FrameType.longType(),
FrameType.longHighType()
@@ -4170,7 +4198,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType(),
FrameType.longType(),
FrameType.longHighType(),
@@ -4217,7 +4245,7 @@
FrameType.longType(),
FrameType.longHighType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.charArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.charArrayType),
FrameType.intType()
})),
new CfNew(options.itemFactory.stringType),
@@ -6154,9 +6182,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Comparator;"))
})),
new CfLoad(ValueType.OBJECT, 2),
@@ -6177,9 +6205,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/Comparator;"))
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -6246,8 +6274,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfIf(If.Type.NE, ValueType.OBJECT, label2),
@@ -6258,8 +6286,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.booleanArrayType),
@@ -6290,8 +6318,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label5,
@@ -6299,8 +6327,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6309,8 +6337,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.byteArrayType),
@@ -6341,8 +6369,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label9,
@@ -6350,8 +6378,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6360,8 +6388,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.charArrayType),
@@ -6392,8 +6420,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label13,
@@ -6401,8 +6429,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6411,8 +6439,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.doubleArrayType),
@@ -6443,8 +6471,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label17,
@@ -6452,8 +6480,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6462,8 +6490,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.floatArrayType),
@@ -6494,8 +6522,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label21,
@@ -6503,8 +6531,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6513,8 +6541,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.intArrayType),
@@ -6545,8 +6573,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label25,
@@ -6554,8 +6582,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6564,8 +6592,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.longArrayType),
@@ -6596,8 +6624,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label29,
@@ -6605,8 +6633,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6615,8 +6643,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.shortArrayType),
@@ -6647,8 +6675,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label33,
@@ -6656,8 +6684,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6666,8 +6694,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceOf(options.itemFactory.createType("[Ljava/lang/Object;")),
@@ -6698,8 +6726,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label37,
@@ -6707,8 +6735,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6717,8 +6745,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -6769,8 +6797,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(1, ValueType.INT),
new CfGoto(label3),
@@ -6779,8 +6807,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfConstNumber(0, ValueType.INT),
label3,
@@ -6788,8 +6816,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -6817,7 +6845,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
182,
@@ -6830,7 +6860,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ }),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
label3),
@@ -6857,13 +6889,17 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ }),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
label3),
@@ -6890,13 +6926,17 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfConstNumber(0, ValueType.INT),
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ }),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
label3),
@@ -6924,8 +6964,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 1),
new CfConstString(options.itemFactory.createString("defaultObj")),
@@ -6966,8 +7006,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
})),
new CfLoad(ValueType.OBJECT, 1),
@@ -7042,8 +7082,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.stringType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfReturn(ValueType.OBJECT),
@@ -7087,8 +7127,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
})),
new CfConstNull(),
@@ -7097,12 +7137,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
}),
new ArrayDeque<>(
- Arrays.asList(FrameType.initialized(options.itemFactory.stringType)))),
+ Arrays.asList(
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)))),
new CfStore(ValueType.OBJECT, 2),
label4,
new CfNew(options.itemFactory.createType("Ljava/lang/NullPointerException;")),
@@ -7122,8 +7163,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
})),
new CfLoad(ValueType.OBJECT, 0),
@@ -7180,8 +7221,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.stringType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
@@ -7196,11 +7237,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.stringType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
}),
new ArrayDeque<>(
- Arrays.asList(FrameType.initialized(options.itemFactory.stringType)))),
+ Arrays.asList(
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)))),
new CfReturn(ValueType.OBJECT),
label3),
ImmutableList.of(),
@@ -7252,10 +7294,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Consumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -7270,10 +7314,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Consumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfReturnVoid(),
label4),
@@ -7327,11 +7373,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalDouble;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -7346,11 +7393,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalDouble;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/DoubleConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfReturnVoid(),
label4),
@@ -7404,11 +7452,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalInt;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -7423,11 +7472,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalInt;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/IntConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfReturnVoid(),
label4),
@@ -7481,11 +7531,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalLong;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -7500,11 +7551,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalLong;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/LongConsumer;")),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Runnable;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Runnable;"))
})),
new CfReturnVoid(),
label4),
@@ -7539,7 +7591,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;"))
})),
new CfConstNumber(0, ValueType.INT),
label2,
@@ -7547,7 +7600,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;"))
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
new CfReturn(ValueType.INT),
@@ -7583,7 +7637,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
})),
new CfConstNumber(0, ValueType.INT),
@@ -7592,7 +7646,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -7629,7 +7683,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
})),
new CfConstNumber(0, ValueType.INT),
@@ -7638,7 +7692,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -7675,7 +7729,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
})),
new CfConstNumber(0, ValueType.INT),
@@ -7684,7 +7738,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
}),
new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
@@ -7735,8 +7789,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;")),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;")),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/function/Supplier;"))
})),
new CfLoad(ValueType.OBJECT, 1),
@@ -7810,7 +7865,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Optional;"))
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Optional;"))
})),
new CfInvoke(
184,
@@ -7870,7 +7926,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalDouble;"))
})),
new CfInvoke(
@@ -7931,7 +7987,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalInt;"))
})),
new CfInvoke(
@@ -7992,7 +8048,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0},
new FrameType[] {
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/OptionalLong;"))
})),
new CfInvoke(
@@ -8136,7 +8192,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
184,
@@ -8151,10 +8209,12 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ }),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/util/stream/Stream;"))))),
new CfReturn(ValueType.OBJECT),
label3),
@@ -8196,7 +8256,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8234,7 +8294,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -8257,7 +8317,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.stringType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
+ })),
new CfConstNumber(1, ValueType.INT),
new CfReturn(ValueType.INT),
label9),
@@ -8302,8 +8364,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/lang/CharSequence;"))
})),
new CfNew(options.itemFactory.stringBuilderType),
@@ -8343,10 +8405,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/lang/CharSequence;")),
- FrameType.initialized(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 3),
@@ -8389,10 +8451,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/lang/CharSequence;")),
- FrameType.initialized(options.itemFactory.stringBuilderType)
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType)
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -8444,8 +8506,9 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;"))
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Iterable;"))
})),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -8504,10 +8567,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;")),
- FrameType.initialized(options.itemFactory.stringBuilderType),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Iterable;")),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Iterator;"))
})),
new CfLoad(ValueType.OBJECT, 3),
new CfInvoke(
@@ -8558,10 +8623,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.charSequenceType),
- FrameType.initialized(options.itemFactory.createType("Ljava/lang/Iterable;")),
- FrameType.initialized(options.itemFactory.stringBuilderType),
- FrameType.initialized(options.itemFactory.createType("Ljava/util/Iterator;"))
+ FrameType.initializedNonNullReference(options.itemFactory.charSequenceType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/lang/Iterable;")),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Ljava/util/Iterator;"))
})),
new CfLoad(ValueType.OBJECT, 2),
new CfInvoke(
@@ -8654,7 +8721,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType), FrameType.intType()
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
@@ -8675,7 +8743,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8686,7 +8754,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8701,7 +8769,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8805,7 +8873,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8831,10 +8899,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 4),
@@ -8860,10 +8928,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
- FrameType.initialized(options.itemFactory.stringBuilderType)
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType)
})),
new CfLoad(ValueType.OBJECT, 3),
new CfInvoke(
@@ -8919,7 +8987,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -8956,7 +9024,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -8980,7 +9048,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -9019,7 +9087,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -9043,7 +9111,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -9100,7 +9168,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -9137,7 +9205,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType(),
FrameType.intType()
@@ -9161,7 +9229,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -9214,7 +9282,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType), FrameType.intType()
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.intType()
})),
new CfLoad(ValueType.INT, 1),
new CfIf(If.Type.LE, ValueType.INT, label7),
@@ -9250,7 +9319,7 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
FrameType.intType(),
FrameType.intType()
})),
@@ -9273,7 +9342,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.stringType), FrameType.intType()
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfConstNumber(0, ValueType.INT),
@@ -9353,12 +9423,12 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.throwableType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
}),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 2),
label4,
@@ -9366,8 +9436,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.throwableType)
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType),
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
})),
new CfReturnVoid(),
label5),
@@ -9430,10 +9500,12 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.throwableType)}),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.throwableType)
+ }),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 1),
label4,
@@ -9467,12 +9539,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
- FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Lsun/misc/Unsafe;")),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
FrameType.longType(),
FrameType.longHighType(),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
@@ -9500,12 +9573,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
- FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("Lsun/misc/Unsafe;")),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
FrameType.longType(),
FrameType.longHighType(),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.objectType)
+ FrameType.initializedNonNullReference(options.itemFactory.objectType),
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
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 9788ee2..a4b79c0 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
@@ -306,7 +306,7 @@
instructions.add(initializedTrueSecond);
instructions.add(
CfFrame.builder()
- .appendLocal(FrameType.initialized(builder.getFactory().objectType))
+ .appendLocal(FrameType.initializedNonNullReference(builder.getFactory().objectType))
.build());
instructions.add(new CfLoad(ValueType.OBJECT, 0));
instructions.add(new CfMonitor(Monitor.Type.EXIT));
@@ -316,8 +316,8 @@
instructions.add(tryCatchTarget);
instructions.add(
CfFrame.builder()
- .appendLocal(FrameType.initialized(builder.getFactory().objectType))
- .push(FrameType.initialized(builder.getFactory().throwableType))
+ .appendLocal(FrameType.initializedNonNullReference(builder.getFactory().objectType))
+ .push(FrameType.initializedNonNullReference(builder.getFactory().throwableType))
.build());
instructions.add(new CfStore(ValueType.OBJECT, 1));
instructions.add(new CfLoad(ValueType.OBJECT, 0));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 4da9973..37451f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
@@ -34,6 +35,7 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.ProgramAdditions;
import com.android.tools.r8.utils.BooleanUtils;
@@ -146,6 +148,20 @@
if (needsDesugaring(invokedMethod, method)) {
prepareDesugarMethodInstruction(invokedMethod, method, programAdditions);
}
+ } else if (instruction.isInvokeDynamic()) {
+ // Starting from Java 17, lambda can use nest based access. We need to generate
+ // bridges for the targeted lambda method.
+ CfInvokeDynamic cfInvokeDynamic = instruction.asInvokeDynamic();
+ LambdaDescriptor lambdaDescriptor =
+ LambdaDescriptor.tryInfer(
+ cfInvokeDynamic.getCallSite(), appView.appInfoForDesugaring(), method);
+ if (lambdaDescriptor != null) {
+ DexMember<?, ?> member = lambdaDescriptor.implHandle.member;
+ if (needsDesugaring(member, method)) {
+ assert member.isDexMethod();
+ prepareDesugarMethodInstruction(member.asDexMethod(), method, programAdditions);
+ }
+ }
}
});
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
index e46b8d6..d3955b6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
@@ -122,9 +122,10 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.classType),
- FrameType.initialized(options.itemFactory.stringType)
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(options.itemFactory.classType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
})),
new CfLoad(ValueType.OBJECT, 2),
new CfConstString(options.itemFactory.createString(";")),
@@ -142,13 +143,14 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.classType),
- FrameType.initialized(options.itemFactory.stringType)
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(options.itemFactory.classType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
}),
new ArrayDeque<>(
Arrays.asList(
- FrameType.initialized(
+ FrameType.initializedNonNullReference(
options.itemFactory.createType("[Ljava/lang/String;"))))),
new CfStore(ValueType.OBJECT, 3),
label3,
@@ -198,11 +200,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.classType),
- FrameType.initialized(options.itemFactory.stringType),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
- FrameType.initialized(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(options.itemFactory.classType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/String;")),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 5),
@@ -267,11 +271,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.classType),
- FrameType.initialized(options.itemFactory.stringType),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
- FrameType.initialized(options.itemFactory.stringBuilderType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(options.itemFactory.classType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/String;")),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType),
FrameType.intType()
})),
new CfIinc(5, 1),
@@ -281,11 +287,13 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
new FrameType[] {
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/Object;")),
- FrameType.initialized(options.itemFactory.classType),
- FrameType.initialized(options.itemFactory.stringType),
- FrameType.initialized(options.itemFactory.createType("[Ljava/lang/String;")),
- FrameType.initialized(options.itemFactory.stringBuilderType)
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ FrameType.initializedNonNullReference(options.itemFactory.classType),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType),
+ FrameType.initializedNonNullReference(
+ options.itemFactory.createType("[Ljava/lang/String;")),
+ FrameType.initializedNonNullReference(options.itemFactory.stringBuilderType)
})),
new CfLoad(ValueType.OBJECT, 4),
new CfConstString(options.itemFactory.createString("]")),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index afeb060..1cf1e36 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -921,20 +921,18 @@
if (options.testing.enableDeadSwitchCaseElimination) {
SwitchCaseEliminator eliminator =
removeUnnecessarySwitchCases(code, theSwitch, iterator, switchCaseAnalyzer);
- if (eliminator != null) {
- if (eliminator.mayHaveIntroducedUnreachableBlocks()) {
- needToRemoveUnreachableBlocks = true;
- }
-
- iterator.previous();
- instruction = iterator.next();
- if (instruction.isGoto()) {
- continue;
- }
-
- assert instruction.isSwitch();
- theSwitch = instruction.asSwitch();
+ if (eliminator.mayHaveIntroducedUnreachableBlocks()) {
+ needToRemoveUnreachableBlocks = true;
}
+
+ iterator.previous();
+ instruction = iterator.next();
+ if (instruction.isGoto()) {
+ continue;
+ }
+
+ assert instruction.isSwitch();
+ theSwitch = instruction.asSwitch();
}
if (theSwitch.isIntSwitch()) {
rewriteIntSwitch(code, blocksIterator, block, iterator, theSwitch.asIntSwitch());
@@ -1111,46 +1109,38 @@
InstructionListIterator iterator,
SwitchCaseAnalyzer switchCaseAnalyzer) {
BasicBlock defaultTarget = theSwitch.fallthroughBlock();
- SwitchCaseEliminator eliminator = null;
+ SwitchCaseEliminator eliminator = new SwitchCaseEliminator(theSwitch, iterator);
BasicBlockBehavioralSubsumption behavioralSubsumption =
new BasicBlockBehavioralSubsumption(appView, code);
// Compute the set of switch cases that can be removed.
+ boolean hasSwitchCaseToDefaultRewrite = false;
AbstractValue switchAbstractValue = theSwitch.value().getAbstractValue(appView, code.context());
for (int i = 0; i < theSwitch.numberOfKeys(); i++) {
BasicBlock targetBlock = theSwitch.targetBlock(i);
if (switchCaseAnalyzer.switchCaseIsAlwaysHit(theSwitch, i)) {
- if (eliminator == null) {
- eliminator = new SwitchCaseEliminator(theSwitch, iterator);
- }
eliminator.markSwitchCaseAsAlwaysHit(i);
break;
}
// This switch case can be removed if the behavior of the target block is equivalent to the
// behavior of the default block, or if the switch case is unreachable.
- if (switchCaseAnalyzer.switchCaseIsUnreachable(theSwitch, switchAbstractValue, i)
- || behavioralSubsumption.isSubsumedBy(targetBlock, defaultTarget)) {
- if (eliminator == null) {
- eliminator = new SwitchCaseEliminator(theSwitch, iterator);
- }
+ if (switchCaseAnalyzer.switchCaseIsUnreachable(theSwitch, switchAbstractValue, i)) {
eliminator.markSwitchCaseForRemoval(i);
+ } else if (behavioralSubsumption.isSubsumedBy(targetBlock, defaultTarget)) {
+ eliminator.markSwitchCaseForRemoval(i);
+ hasSwitchCaseToDefaultRewrite = true;
}
}
- if (eliminator == null || eliminator.isFallthroughLive()) {
- if (switchCaseAnalyzer.switchFallthroughIsNeverHit(theSwitch, switchAbstractValue)) {
- if (eliminator == null) {
- eliminator = new SwitchCaseEliminator(theSwitch, iterator);
- }
- eliminator.markSwitchFallthroughAsNeverHit();
- }
+ if (eliminator.isFallthroughLive()
+ && !hasSwitchCaseToDefaultRewrite
+ && switchCaseAnalyzer.switchFallthroughIsNeverHit(theSwitch, switchAbstractValue)) {
+ eliminator.markSwitchFallthroughAsNeverHit();
}
- if (eliminator != null) {
- eliminator.optimize();
- }
+ eliminator.optimize();
return eliminator;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
index 527fb13..3a20e47 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -202,7 +202,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.intType(),
- FrameType.initialized(options.itemFactory.intArrayType),
+ FrameType.initializedNonNullReference(options.itemFactory.intArrayType),
FrameType.intType()
})),
new CfLoad(ValueType.INT, 2),
@@ -224,7 +224,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.intType(), FrameType.initialized(options.itemFactory.intArrayType)
+ FrameType.intType(),
+ FrameType.initializedNonNullReference(options.itemFactory.intArrayType)
})),
new CfLoad(ValueType.OBJECT, 1),
new CfReturn(ValueType.OBJECT),
@@ -298,7 +299,8 @@
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1},
new FrameType[] {
- FrameType.intType(), FrameType.initialized(options.itemFactory.stringType)
+ FrameType.intType(),
+ FrameType.initializedNonNullReference(options.itemFactory.stringType)
})),
new CfReturnVoid(),
label3),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
index 5ecb6bb..05a91e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
@@ -38,7 +38,11 @@
InstructionListIterator iterator,
Instruction instruction,
StringBuilderOracle oracle) {
- assert oracle.isModeledStringBuilderInstruction(instruction);
+ assert oracle.isModeledStringBuilderInstruction(
+ instruction,
+ value ->
+ value.getType().isClassType()
+ && oracle.isStringBuilderType(value.getType().asClassType().getClassType()));
if (oracle.isAppend(instruction) && instruction.outValue() != null) {
// Append will return the string builder instance. Before removing, ensure that
// all users of the output values uses the receiver.
@@ -70,7 +74,7 @@
InstructionListIterator iterator,
Instruction instruction,
StringBuilderOracle oracle) {
- assert oracle.isToString(instruction);
+ assert oracle.isToString(instruction, instruction.getFirstOperand());
iterator.replaceCurrentInstructionWithConstString(appView, code, replacement);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index caf8432..830516e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -44,6 +44,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -155,7 +156,7 @@
DFSNodeWithState<BasicBlock, StringBuilderGraphState> node,
Function<BasicBlock, DFSNodeWithState<BasicBlock, StringBuilderGraphState>>
childNodeConsumer) {
- Map<Value, StringBuilderNode> currentRoots = new IdentityHashMap<>();
+ Map<Value, StringBuilderNode> currentRoots = new LinkedHashMap<>();
Map<Value, StringBuilderNode> currentTails = new IdentityHashMap<>();
BasicBlock block = node.getNode();
StringBuilderEscapeState previousState = analysis.computeBlockEntryState(block);
@@ -229,7 +230,8 @@
if (instruction.isAssume()) {
return;
}
- if (oracle.isModeledStringBuilderInstruction(instruction)) {
+ if (oracle.isModeledStringBuilderInstruction(
+ instruction, escapeState::isLiveStringBuilder)) {
createNodesForStringBuilderInstruction(instruction, escapeState, nodeConsumer);
} else {
for (Value newEscapedValue : escapeState.getNewlyEscaped()) {
@@ -291,7 +293,7 @@
escapeState,
actual -> nodeConsumer.accept(actual, appendNode),
escaped -> nodeConsumer.accept(escaped, createMutateNode()));
- } else if (oracle.isToString(instruction)) {
+ } else if (oracle.isToString(instruction, receiver)) {
visitStringBuilderValues(
receiver,
escapeState,
@@ -433,11 +435,12 @@
}
}
if (state.isPartOfLoop) {
- for (Value value : state.roots.keySet()) {
- LoopNode loopNode = StringBuilderNode.createLoopNode();
- loopNode.addSuccessor(state.roots.get(value));
- state.roots.put(value, loopNode);
- }
+ state.roots.replaceAll(
+ (value, currentRoot) -> {
+ LoopNode loopNode = StringBuilderNode.createLoopNode();
+ loopNode.addSuccessor(currentRoot);
+ return loopNode;
+ });
}
return TraversalContinuation.doContinue(state);
}
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
index ff4a62a..bc92a0f 100644
--- 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
@@ -53,16 +53,30 @@
public TransferFunctionResult<StringBuilderEscapeState> apply(
Instruction instruction, StringBuilderEscapeState state) {
StringBuilderEscapeState.Builder builder = state.builder();
- boolean isStringBuilderInstruction = oracle.isModeledStringBuilderInstruction(instruction);
+ boolean isStringBuilderInstruction =
+ oracle.isModeledStringBuilderInstruction(instruction, state::isLiveStringBuilder);
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);
}
}
}
+ if (isStringBuilderInstruction) {
+ if (instruction.isInvokeMethodWithReceiver()) {
+ Value firstOperand = instruction.getFirstOperand();
+ if (!builder.getLiveStringBuilders().contains(firstOperand)) {
+ // We can have constant NULL being the first operand, which we have not marked as
+ // a live string builder.
+ assert firstOperand.getAliasedValue().isConstZero();
+ builder.addLiveStringBuilder(firstOperand);
+ }
+ } else {
+ assert instruction.isNewInstance();
+ }
+ }
assert !isStringBuilderInstruction
+ || instruction.isNewInstance()
|| builder.getLiveStringBuilders().contains(instruction.getFirstOperand());
Value outValue = instruction.outValue();
if (outValue != 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
index acd5c47..22d5c70 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
@@ -39,8 +39,8 @@
ImplicitToStringNode getImplicitToStringNode();
}
- private final Set<StringBuilderNode> successors = Sets.newIdentityHashSet();
- private final Set<StringBuilderNode> predecessors = Sets.newIdentityHashSet();
+ private final Set<StringBuilderNode> successors = Sets.newLinkedHashSet();
+ private final Set<StringBuilderNode> predecessors = Sets.newLinkedHashSet();
// 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.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
index 000ca52..01be10c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
@@ -246,16 +246,12 @@
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) {
+ removeNode = false;
+ if (currentNode.isSplitReferenceNode()) {
+ removeNode = currentNode.getSuccessors().isEmpty() || currentNode.hasSinglePredecessor();
+ } else if (currentNode.isAppendNode() && !isEscaping) {
AppendNode appendNode = currentNode.asAppendNode();
boolean canRemoveIfNoInspectionOrMaterializing =
!munchingState.inspectingCapacity.contains(root)
@@ -271,8 +267,7 @@
appendNode.getInstruction(), RemoveStringBuilderAction.getInstance());
removeNode = true;
}
- }
- if (currentNode.isInitNode()
+ } else if (currentNode.isInitNode()
&& currentNode.asInitNode().hasConstantArgument()
&& currentNode.hasSinglePredecessor()
&& currentNode.getSinglePredecessor().isNewInstanceNode()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
index b6c0b4f..dd49ae4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
@@ -12,10 +12,10 @@
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;
+import java.util.function.Predicate;
/**
* The {@link StringBuilderOracle} can answer if an instruction is of particular interest to the
@@ -23,13 +23,14 @@
*/
interface StringBuilderOracle {
- boolean isModeledStringBuilderInstruction(Instruction instruction);
+ boolean isModeledStringBuilderInstruction(
+ Instruction instruction, Predicate<Value> isLiveStringBuilder);
boolean hasStringBuilderType(Value value);
boolean isStringBuilderType(DexType type);
- boolean isToString(Instruction instruction);
+ boolean isToString(Instruction instruction, Value value);
String getConstantArgument(Instruction instruction);
@@ -50,13 +51,18 @@
}
@Override
- public boolean isModeledStringBuilderInstruction(Instruction instruction) {
+ public boolean isModeledStringBuilderInstruction(
+ Instruction instruction, Predicate<Value> isLiveStringBuilder) {
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);
+ if (isStringBuildingMethod(factory.stringBuilderMethods, invokedMethod)
+ || isStringBuildingMethod(factory.stringBufferMethods, invokedMethod)) {
+ return true;
+ }
+ return invokedMethod == factory.objectMembers.toString
+ && isLiveStringBuilder.test(instruction.getFirstOperand());
}
return false;
}
@@ -80,14 +86,18 @@
}
@Override
- public boolean isToString(Instruction instruction) {
- if (!instruction.isInvokeMethodWithReceiver()) {
+ public boolean isToString(Instruction instruction, Value value) {
+ if (!instruction.isInvokeVirtual()) {
return false;
}
- InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+ InvokeVirtual invoke = instruction.asInvokeVirtual();
+ if (invoke.getReceiver() != value) {
+ return false;
+ }
DexMethod invokedMethod = invoke.getInvokedMethod();
return factory.stringBuilderMethods.toString == invokedMethod
- || factory.stringBufferMethods.toString == invokedMethod;
+ || factory.stringBufferMethods.toString == invokedMethod
+ || factory.objectMembers.toString == invokedMethod;
}
@Override
@@ -151,13 +161,16 @@
return false;
}
DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ if (factory.stringBuilderMethods.isAppendSubArrayMethod(invokedMethod)
+ || factory.stringBufferMethods.isAppendSubArrayMethod(invokedMethod)) {
+ return false;
+ }
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;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
index 162a1cc..0cb8e39 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -66,7 +66,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfReturnVoid(),
label3),
ImmutableList.of(),
@@ -171,7 +173,9 @@
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0},
- new FrameType[] {FrameType.initialized(options.itemFactory.objectType)})),
+ new FrameType[] {
+ FrameType.initializedNonNullReference(options.itemFactory.objectType)
+ })),
new CfReturnVoid(),
label3),
ImmutableList.of(),
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 fb04da0..a32d51e 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
@@ -91,9 +91,9 @@
CfFrame frame =
CfFrame.builder()
.appendLocal(FrameType.initialized(typeArray))
- .appendLocal(FrameType.initialized(factory.intType))
+ .appendLocal(FrameType.intType())
.appendLocal(FrameType.initialized(convertedTypeArray))
- .appendLocal(FrameType.initialized(factory.intType))
+ .appendLocal(FrameType.intType())
.build();
// int t1 = arg.length;
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 ab83fdb..707bf1c 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -596,10 +596,13 @@
case ENUM:
DexField enumField = value.asDexValueEnum().getValue();
+ // This must not be renamed, as the Java runtime will use Enum.valueOf to find the enum's
+ // referenced in annotations. See b/236691999 for details.
+ assert getNamingLens().lookupName(enumField) == enumField.name;
visitor.visitEnum(
name,
getNamingLens().lookupDescriptor(enumField.getType()).toString(),
- getNamingLens().lookupName(enumField).toString());
+ enumField.name.toString());
break;
case FIELD:
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 9591d26..668f6de 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -418,7 +418,7 @@
return mapVersions;
}
- public MapVersionMappingInformation getFirstMappingInformation() {
+ public MapVersionMappingInformation getFirstMapVersionInformation() {
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 352c555..aae3f42 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -365,6 +365,30 @@
return fieldMembers.values();
}
+ public void visitAllFullyQualifiedReferences(Consumer<String> consumer) {
+ mappedFieldNamingsByName
+ .values()
+ .forEach(
+ mappedFields ->
+ mappedFields.forEach(
+ mappedField -> {
+ if (mappedField.renamedSignature.isQualified()) {
+ consumer.accept(mappedField.renamedSignature.toHolderFromQualified());
+ }
+ }));
+ mappedRangesByRenamedName
+ .values()
+ .forEach(
+ mappedRanges -> {
+ mappedRanges.mappedRanges.forEach(
+ mappedRange -> {
+ if (mappedRange.signature.isQualified()) {
+ consumer.accept(mappedRange.signature.toHolderFromQualified());
+ }
+ });
+ });
+ }
+
@Override
public <T extends Throwable> void forAllMethodNaming(
ThrowingConsumer<MemberNaming, T> consumer) throws T {
diff --git a/src/main/java/com/android/tools/r8/naming/MapVersion.java b/src/main/java/com/android/tools/r8/naming/MapVersion.java
index a23196c..ae93504 100644
--- a/src/main/java/com/android/tools/r8/naming/MapVersion.java
+++ b/src/main/java/com/android/tools/r8/naming/MapVersion.java
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.utils.structural.Ordered;
+@Keep
public enum MapVersion implements Ordered<MapVersion> {
MAP_VERSION_NONE("none"),
MAP_VERSION_1_0("1.0"),
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 3684506..134b2d9 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ValueType;
import java.util.function.BiFunction;
@@ -97,6 +98,11 @@
}
@Override
+ public CfFrameState push(CfAnalysisConfig config, TypeElement type) {
+ return this;
+ }
+
+ @Override
public CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType) {
return this;
}
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 0a2bec0..bd636c3 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
@@ -19,4 +19,6 @@
int getMaxStack();
boolean isImmediateSuperClassOfCurrentContext(DexType type);
+
+ boolean isStrengthenFramesEnabled();
}
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 b136b9b..437d01d 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
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
@@ -173,15 +174,18 @@
@SuppressWarnings("InconsistentOverloads")
public final CfFrameState popObject(
+ AppView<?> appView,
DexType expectedType,
CfAnalysisConfig config,
BiFunction<CfFrameState, PreciseFrameType, CfFrameState> fn) {
CfAssignability assignability = config.getAssignability();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
return pop(
(state, head) ->
head.isObject()
&& assignability.isAssignable(
- head.getObjectType(config.getCurrentContext().getHolderType()),
+ head.getObjectType(
+ dexItemFactory, config.getCurrentContext().getHolderType()),
expectedType)
? fn.apply(state, head)
: errorUnexpectedStack(head, expectedType));
@@ -224,6 +228,8 @@
public abstract CfFrameState push(CfAnalysisConfig config, DexType type);
+ public abstract CfFrameState push(CfAnalysisConfig config, TypeElement type);
+
public abstract CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType);
public final CfFrameState push(
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 ae2e594..bc44d01 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
@@ -5,12 +5,17 @@
package com.android.tools.r8.optimize.interfaces.analysis;
import com.android.tools.r8.cf.code.CfAssignability;
+import com.android.tools.r8.cf.code.CfFrameVerifier;
+import com.android.tools.r8.cf.code.CfFrameVerifier.StackMapStatus;
+import com.android.tools.r8.cf.code.CfFrameVerifierEventConsumer;
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.CfCodeDiagnostics;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -23,17 +28,25 @@
import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfBlock;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfControlFlowGraph;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfIntraproceduralDataflowAnalysis;
+import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.OpenClosedInterfacesOptions;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
public class CfOpenClosedInterfacesAnalysis {
@@ -41,18 +54,20 @@
private final CfAssignability assignability;
private final InternalOptions options;
+ private final Set<DexClass> openInterfaces = Sets.newConcurrentHashSet();
+
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;
+ this.options = appView.options();
}
public boolean run(ExecutorService executorService) throws ExecutionException {
processClasses(executorService);
+ setClosedInterfaces();
reportUnverifiableCodeDiagnostics();
return true;
}
@@ -62,19 +77,96 @@
}
private void processClass(DexProgramClass clazz) {
- clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, this::processMethod);
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode, method -> openInterfaces.addAll(processMethod(method)));
}
- private void processMethod(ProgramMethod method) {
+ private Set<DexClass> processMethod(ProgramMethod method) {
Code code = method.getDefinition().getCode();
if (!code.isCfCode()) {
assert code.isDefaultInstanceInitializerCode() || code.isDexCode() || code.isThrowNullCode();
- return;
+ return Collections.emptySet();
}
-
CfCode cfCode = code.asCfCode();
- CfControlFlowGraph cfg = CfControlFlowGraph.create(cfCode, options);
- TransferFunction transfer = new TransferFunction(method);
+ CfAnalysisConfig config = createConfig(method, cfCode);
+ CfOpenClosedInterfacesAnalysisHelper helper =
+ new CfOpenClosedInterfacesAnalysisHelper(appView, method, unverifiableCodeDiagnostics);
+ if (runLinearScan(method, cfCode, config, helper).isNotPresent()) {
+ runFixpoint(method, cfCode, config, helper);
+ }
+ return helper.getOpenInterfaces();
+ }
+
+ private CfAnalysisConfig createConfig(ProgramMethod method, CfCode code) {
+ return new CfAnalysisConfig() {
+
+ @Override
+ public CfAssignability getAssignability() {
+ return assignability;
+ }
+
+ @Override
+ public DexMethod getCurrentContext() {
+ return method.getReference();
+ }
+
+ @Override
+ public int getMaxLocals() {
+ return code.getMaxLocals();
+ }
+
+ @Override
+ public int getMaxStack() {
+ return code.getMaxStack();
+ }
+
+ @Override
+ public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
+ return type == method.getHolder().getSuperType();
+ }
+
+ @Override
+ public boolean isStrengthenFramesEnabled() {
+ return true;
+ }
+ };
+ }
+
+ private StackMapStatus runLinearScan(
+ ProgramMethod method,
+ CfCode code,
+ CfAnalysisConfig config,
+ CfOpenClosedInterfacesAnalysisHelper helper) {
+ CfFrameVerifierEventConsumer eventConsumer =
+ new CfFrameVerifierEventConsumer() {
+
+ @Override
+ public void acceptError(CfCodeDiagnostics diagnostics) {
+ helper.registerUnverifiableCodeWithFrames(diagnostics);
+ }
+
+ @Override
+ public void acceptInstructionState(CfInstruction instruction, CfFrameState state) {
+ helper.processInstruction(instruction, state);
+ }
+ };
+ CfFrameVerifier verifier =
+ CfFrameVerifier.builder(appView, code, method)
+ .setConfig(config)
+ .setEventConsumer(eventConsumer)
+ .build();
+ StackMapStatus stackMapStatus = verifier.run();
+ assert stackMapStatus.isValid() || helper.getOpenInterfaces().isEmpty();
+ return stackMapStatus;
+ }
+
+ private void runFixpoint(
+ ProgramMethod method,
+ CfCode code,
+ CfAnalysisConfig config,
+ CfOpenClosedInterfacesAnalysisHelper helper) {
+ CfControlFlowGraph cfg = CfControlFlowGraph.create(code, options);
+ TransferFunction transfer = new TransferFunction(config, method);
CfIntraproceduralDataflowAnalysis<CfFrameState> analysis =
new CfIntraproceduralDataflowAnalysis<>(appView, CfFrameState.bottom(), cfg, transfer);
DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock());
@@ -84,23 +176,19 @@
continue;
}
CfFrameState state = analysis.computeBlockEntryState(block);
+ if (state.isError()) {
+ helper.registerUnverifiableCode(method, 0, state.asError());
+ return;
+ }
do {
for (int instructionIndex = block.getFirstInstructionIndex();
instructionIndex <= block.getLastInstructionIndex();
instructionIndex++) {
- // TODO(b/214496607): Determine open interfaces.
- CfInstruction instruction = cfCode.getInstruction(instructionIndex);
+ CfInstruction instruction = code.getInstruction(instructionIndex);
+ helper.processInstruction(instruction, state);
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()));
- }
+ helper.registerUnverifiableCode(method, instructionIndex, state.asError());
return;
}
}
@@ -113,6 +201,43 @@
}
}
+ private void setClosedInterfaces() {
+ // If open interfaces are not allowed and there are one or more suppressions, we should find at
+ // least one open interface.
+ OpenClosedInterfacesOptions openClosedInterfacesOptions =
+ options.getOpenClosedInterfacesOptions();
+ assert openClosedInterfacesOptions.isOpenInterfacesAllowed()
+ || !openClosedInterfacesOptions.hasSuppressions()
+ || !openInterfaces.isEmpty()
+ : "Expected to find at least one open interface";
+
+ includeParentOpenInterfaces();
+
+ appView.setOpenClosedInterfacesCollection(
+ new NonEmptyOpenClosedInterfacesCollection(
+ openInterfaces.stream()
+ .map(DexClass::getType)
+ .collect(
+ Collectors.toCollection(
+ () -> SetUtils.newIdentityHashSet(openInterfaces.size())))));
+ }
+
+ private void includeParentOpenInterfaces() {
+ // This includes all parent interfaces of each open interface in the set of open interfaces,
+ // by using the open interfaces as the seen set.
+ WorkList<DexClass> worklist = WorkList.newWorkList(openInterfaces);
+ worklist.addAllIgnoringSeenSet(openInterfaces);
+ while (worklist.hasNext()) {
+ DexClass openInterface = worklist.next();
+ for (DexType indirectOpenInterfaceType : openInterface.getInterfaces()) {
+ DexClass indirectOpenInterfaceDefinition = appView.definitionFor(indirectOpenInterfaceType);
+ if (indirectOpenInterfaceDefinition != null) {
+ worklist.addIfNotSeen(indirectOpenInterfaceDefinition);
+ }
+ }
+ }
+ }
+
private void reportUnverifiableCodeDiagnostics() {
Reporter reporter = appView.reporter();
List<ProgramMethod> methods = new ArrayList<>(unverifiableCodeDiagnostics.size());
@@ -124,43 +249,11 @@
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();
- }
-
- @Override
- public int getMaxLocals() {
- return maxLocals;
- }
-
- @Override
- public int getMaxStack() {
- return maxStack;
- }
-
- @Override
- public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
- return type == context.getHolder().getSuperType();
- }
- };
+ TransferFunction(CfAnalysisConfig config, ProgramMethod context) {
+ this.config = config;
this.context = context;
}
@@ -180,7 +273,9 @@
} else {
initialState =
initialState.storeLocal(
- localIndex, FrameType.initialized(context.getHolderType()), config);
+ localIndex,
+ FrameType.initializedNonNullReference(context.getHolderType()),
+ config);
}
localIndex++;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysisHelper.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysisHelper.java
new file mode 100644
index 0000000..515fa4e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysisHelper.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.analysis;
+
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStaticFieldWrite;
+import com.android.tools.r8.cf.code.frame.FrameType;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCodeDiagnostics;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
+import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+class CfOpenClosedInterfacesAnalysisHelper {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory dexItemFactory;
+ private final ProgramMethod method;
+ private final InternalOptions options;
+
+ private final Set<DexClass> openInterfaces = Sets.newIdentityHashSet();
+ private final ProgramMethodMap<UnverifiableCfCodeDiagnostic> unverifiableCodeDiagnostics;
+
+ CfOpenClosedInterfacesAnalysisHelper(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod method,
+ ProgramMethodMap<UnverifiableCfCodeDiagnostic> unverifiableCodeDiagnostics) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.method = method;
+ this.options = appView.options();
+ this.unverifiableCodeDiagnostics = unverifiableCodeDiagnostics;
+ }
+
+ Set<DexClass> getOpenInterfaces() {
+ return openInterfaces;
+ }
+
+ void processInstruction(CfInstruction instruction, CfFrameState state) {
+ assert !state.isError();
+ if (state.isBottom()) {
+ // Unreachable.
+ return;
+ }
+ assert state.isConcrete();
+ ConcreteCfFrameState concreteState = state.asConcrete();
+ if (instruction.isArrayStore()) {
+ processArrayStore(instruction.asArrayStore(), concreteState);
+ } else if (instruction.isInstanceFieldPut()) {
+ processInstanceFieldPut(instruction.asInstanceFieldPut(), concreteState);
+ } else if (instruction.isInvoke()) {
+ processInvoke(instruction.asInvoke(), concreteState);
+ } else if (instruction.isReturn() && !instruction.isReturnVoid()) {
+ processReturn(concreteState);
+ } else if (instruction.isStaticFieldPut()) {
+ processStaticFieldPut(instruction.asStaticFieldPut(), concreteState);
+ }
+ }
+
+ private void processArrayStore(CfArrayStore arrayStore, ConcreteCfFrameState state) {
+ if (!arrayStore.getType().isObject()) {
+ return;
+ }
+ state.peekStackElements(
+ 3,
+ stack -> {
+ FrameType array = stack.peekFirst();
+ FrameType value = stack.peekLast();
+ if (array.isInitializedNonNullReferenceType()) {
+ ReferenceTypeElement arrayType =
+ array.asInitializedNonNullReferenceType().getInitializedTypeWithInterfaces(appView);
+ if (arrayType.isArrayType()) {
+ processAssignment(value, arrayType.asArrayType().getMemberType());
+ } else {
+ assert false;
+ }
+ } else {
+ assert false;
+ }
+ },
+ options);
+ }
+
+ private void processInstanceFieldPut(
+ CfInstanceFieldWrite instanceFieldPut, ConcreteCfFrameState state) {
+ state.peekStackElement(
+ head -> processAssignment(head, instanceFieldPut.getField().getType()), options);
+ }
+
+ private void processInvoke(CfInvoke invoke, ConcreteCfFrameState state) {
+ DexMethod invokedMethod = invoke.getMethod();
+ state.peekStackElements(
+ invokedMethod.getNumberOfArguments(invoke.isInvokeStatic()),
+ arguments -> {
+ int argumentIndex = 0;
+ for (FrameType argument : arguments) {
+ DexType parameter =
+ invokedMethod.getArgumentType(argumentIndex, invoke.isInvokeStatic());
+ processAssignment(argument, parameter);
+ argumentIndex++;
+ }
+ },
+ options);
+ }
+
+ private void processReturn(ConcreteCfFrameState state) {
+ state.peekStackElement(head -> processAssignment(head, method.getReturnType()), options);
+ }
+
+ private void processStaticFieldPut(
+ CfStaticFieldWrite staticFieldPut, ConcreteCfFrameState state) {
+ state.peekStackElement(
+ head -> processAssignment(head, staticFieldPut.getField().getType()), options);
+ }
+
+ private void processAssignment(FrameType fromType, DexType toType) {
+ if (fromType.isInitializedNonNullReferenceType()) {
+ processAssignment(
+ fromType.asInitializedNonNullReferenceType().getInitializedTypeWithInterfaces(appView),
+ toType);
+ }
+ }
+
+ private void processAssignment(FrameType fromType, TypeElement toType) {
+ if (fromType.isInitializedNonNullReferenceType()) {
+ processAssignment(
+ fromType.asInitializedNonNullReferenceType().getInitializedTypeWithInterfaces(appView),
+ toType);
+ }
+ }
+
+ private void processAssignment(ReferenceTypeElement fromType, DexType toType) {
+ processAssignment(fromType, toType.toTypeElement(appView));
+ }
+
+ private void processAssignment(TypeElement fromType, TypeElement toType) {
+ // If the type is an interface type, then check that the assigned value is a subtype of the
+ // interface type, or mark the interface as open.
+ if (!toType.isClassType()) {
+ return;
+ }
+ ClassTypeElement toClassType = toType.asClassType();
+ if (toClassType.getClassType() != dexItemFactory.objectType) {
+ return;
+ }
+ InterfaceCollection interfaceCollection = toClassType.getInterfaces();
+ interfaceCollection.forEachKnownInterface(
+ knownInterfaceType -> {
+ DexClass knownInterface = appView.definitionFor(knownInterfaceType);
+ if (knownInterface == null) {
+ return;
+ }
+ assert knownInterface.isInterface();
+ if (fromType.lessThanOrEqualUpToNullability(toType, appView)) {
+ return;
+ }
+ assert verifyOpenInterfaceWitnessIsSuppressed(fromType, knownInterface);
+ openInterfaces.add(knownInterface);
+ });
+ }
+
+ void registerUnverifiableCode(
+ ProgramMethod method, int instructionIndex, ErroneousCfFrameState state) {
+ if (options.getCfCodeAnalysisOptions().isUnverifiableCodeReportingEnabled()) {
+ unverifiableCodeDiagnostics.put(
+ method,
+ new UnverifiableCfCodeDiagnostic(
+ method.getMethodReference(),
+ instructionIndex,
+ state.getMessage(),
+ method.getOrigin()));
+ }
+ openInterfaces.clear();
+ }
+
+ void registerUnverifiableCodeWithFrames(CfCodeDiagnostics diagnostics) {
+ if (options.getCfCodeAnalysisOptions().isUnverifiableCodeReportingEnabled()) {
+ unverifiableCodeDiagnostics.put(
+ method,
+ new UnverifiableCfCodeDiagnostic(
+ method.getMethodReference(),
+ -1,
+ diagnostics.getDiagnosticMessage(),
+ method.getOrigin()));
+ }
+ openInterfaces.clear();
+ }
+
+ private boolean verifyOpenInterfaceWitnessIsSuppressed(
+ TypeElement valueType, DexClass openInterface) {
+ assert options.getOpenClosedInterfacesOptions().isSuppressed(appView, valueType, openInterface)
+ : "Unexpected open interface "
+ + openInterface.getTypeName()
+ + " (assignment: "
+ + valueType
+ + ")";
+ return true;
+ }
+}
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 531db8b..0664659 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
@@ -17,13 +17,16 @@
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.cf.code.frame.WidePrimitiveFrameType;
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;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FunctionUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -35,6 +38,7 @@
import java.util.Iterator;
import java.util.Objects;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
import java.util.function.UnaryOperator;
public class ConcreteCfFrameState extends CfFrameState {
@@ -78,6 +82,9 @@
if (assignabilityResult.isFailed()) {
return error(assignabilityResult.asFailed().getMessage());
}
+ if (config.isStrengthenFramesEnabled()) {
+ return this;
+ }
CfFrame frameCopy = frame.mutableCopy();
return new ConcreteCfFrameState(
frameCopy.getMutableLocals(), frameCopy.getMutableStack(), stackHeight);
@@ -135,6 +142,29 @@
return new ConcreteCfFrameState(locals, newStack, stackHeight);
}
+ public void peekStackElement(Consumer<PreciseFrameType> consumer, InternalOptions options) {
+ if (!stack.isEmpty()) {
+ consumer.accept(stack.peekLast());
+ } else {
+ assert options.getTestingOptions().allowTypeErrors;
+ }
+ }
+
+ public void peekStackElements(
+ int number, Consumer<Deque<PreciseFrameType>> consumer, InternalOptions options) {
+ if (stack.size() >= number) {
+ Deque<PreciseFrameType> result = new ArrayDeque<>(number);
+ Iterator<PreciseFrameType> iterator = stack.descendingIterator();
+ while (iterator.hasNext() && number > 0) {
+ result.addFirst(iterator.next());
+ number--;
+ }
+ consumer.accept(result);
+ } else {
+ assert options.getTestingOptions().allowTypeErrors;
+ }
+ }
+
@Override
public CfFrameState pop() {
return pop(FunctionUtils::getFirst);
@@ -202,11 +232,23 @@
}
private static boolean isArrayTypeOrNull(FrameType frameType) {
- if (frameType.isInitializedReferenceType()
- && frameType.asInitializedReferenceType().getInitializedType().isArrayType()) {
- return true;
+ if (frameType.isInitializedReferenceType()) {
+ if (frameType.isNullType()) {
+ return true;
+ } else if (frameType.isInitializedNonNullReferenceTypeWithInterfaces()) {
+ return frameType
+ .asInitializedNonNullReferenceTypeWithInterfaces()
+ .getInitializedTypeWithInterfaces()
+ .isArrayType();
+ } else {
+ assert frameType.isInitializedNonNullReferenceTypeWithoutInterfaces();
+ return frameType
+ .asInitializedNonNullReferenceTypeWithoutInterfaces()
+ .getInitializedType()
+ .isArrayType();
+ }
}
- return frameType.isNullType();
+ return false;
}
@Override
@@ -244,6 +286,11 @@
}
@Override
+ public CfFrameState push(CfAnalysisConfig config, TypeElement type) {
+ return push(config, FrameType.initialized(type));
+ }
+
+ @Override
public CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType) {
int newStackHeight = stackHeight + frameType.getWidth();
if (newStackHeight > config.getMaxStack()) {
@@ -270,7 +317,7 @@
ArrayDeque<PreciseFrameType> newStack = new ArrayDeque<>();
int newStackHeight = 0;
return new ConcreteCfFrameState(newLocals, newStack, newStackHeight)
- .push(config, FrameType.initialized(guard));
+ .push(config, FrameType.initializedNonNullReference(guard));
}
@Override
@@ -345,32 +392,18 @@
ObjectBidirectionalIterator<Entry<FrameType>> otherIterator =
locals.int2ObjectEntrySet().iterator();
while (iterator.hasNext() && otherIterator.hasNext()) {
- Entry<FrameType> entry = nextLocal(iterator);
+ Entry<FrameType> entry = iterator.next();
int localIndex = entry.getIntKey();
FrameType frameType = entry.getValue();
- Entry<FrameType> otherEntry = nextLocal(otherIterator);
+ Entry<FrameType> otherEntry = otherIterator.next();
int otherLocalIndex = otherEntry.getIntKey();
FrameType otherFrameType = otherEntry.getValue();
if (localIndex < otherLocalIndex) {
- joinLocalsWithDifferentIndices(
- localIndex,
- frameType,
- otherLocalIndex,
- otherFrameType,
- iterator,
- otherIterator,
- builder);
+ joinLocalsWithDifferentIndices(localIndex, otherLocalIndex, otherIterator, builder);
} else if (otherLocalIndex < localIndex) {
- joinLocalsWithDifferentIndices(
- otherLocalIndex,
- otherFrameType,
- localIndex,
- frameType,
- otherIterator,
- iterator,
- builder);
+ joinLocalsWithDifferentIndices(otherLocalIndex, localIndex, iterator, builder);
} else {
joinLocalsWithSameIndex(
localIndex, frameType, otherFrameType, iterator, otherIterator, appView, builder);
@@ -382,31 +415,12 @@
private void joinLocalsWithDifferentIndices(
int localIndex,
- FrameType frameType,
int otherLocalIndex,
- FrameType otherFrameType,
- ObjectBidirectionalIterator<Entry<FrameType>> iterator,
ObjectBidirectionalIterator<Entry<FrameType>> otherIterator,
CfFrame.Builder builder) {
assert localIndex < otherLocalIndex;
-
- // Check if the smaller local does not overlap with the larger local.
- if (frameType.isSingle() || localIndex + 1 < otherLocalIndex) {
- setLocalToTop(localIndex, frameType, builder);
- previousLocal(otherIterator);
- return;
- }
-
- // The smaller local is a wide that overlaps with the larger local.
- setLocalToTop(localIndex, frameType, builder);
-
- // If the larger local is a wide, then its high part is no longer usable. We also need to handle
- // overlapping of the other local and the next local in the iterator.
- if (otherFrameType.isWide()) {
- int lastLocalIndexMarkedTop = otherLocalIndex + 1;
- setSingleLocalToTop(lastLocalIndexMarkedTop, builder);
- handleOverlappingLocals(lastLocalIndexMarkedTop, iterator, otherIterator, builder);
- }
+ setSingleLocalToTop(localIndex, builder);
+ otherIterator.previous();
}
private void joinLocalsWithSameIndex(
@@ -422,16 +436,19 @@
joinSingleLocalsWithSameIndex(
localIndex, frameType.asSingle(), otherFrameType.asSingle(), appView, builder);
} else {
- setWideLocalToTop(localIndex, builder);
- handleOverlappingLocals(localIndex + 1, iterator, otherIterator, builder);
+ joinSingleAndWideLocalsWithSameIndex(localIndex, builder);
}
} else {
if (otherFrameType.isWide()) {
joinWideLocalsWithSameIndex(
- localIndex, frameType.asWide(), otherFrameType.asWide(), builder);
+ localIndex,
+ frameType.asWidePrimitive(),
+ otherFrameType.asWidePrimitive(),
+ iterator,
+ otherIterator,
+ builder);
} else {
- setWideLocalToTop(localIndex, builder);
- handleOverlappingLocals(localIndex + 1, otherIterator, iterator, builder);
+ joinSingleAndWideLocalsWithSameIndex(localIndex, builder);
}
}
}
@@ -445,103 +462,78 @@
builder.store(localIndex, frameType.join(appView, otherFrameType));
}
- private void joinWideLocalsWithSameIndex(
- int localIndex,
- WideFrameType frameType,
- WideFrameType otherFrameType,
- CfFrame.Builder builder) {
- WideFrameType join = frameType.join(otherFrameType);
- if (join.isPrecise()) {
- builder.store(localIndex, join);
- } else {
- assert join.isTwoWord();
- setWideLocalToTop(localIndex, builder);
- }
+ private void joinSingleAndWideLocalsWithSameIndex(int localIndex, CfFrame.Builder builder) {
+ setSingleLocalToTop(localIndex, builder);
}
- // TODO(b/231521474): By splitting each wide type into single left/right types, the join of each
- // (single) local index can be determined by looking at only locals[i] and otherLocals[i] (i.e.,
- // there is no carry-over). Thus this entire method could be avoided.
- private void handleOverlappingLocals(
- int lastLocalIndexMarkedTop,
+ private void joinWideLocalsWithSameIndex(
+ int localIndex,
+ WidePrimitiveFrameType frameType,
+ WidePrimitiveFrameType otherFrameType,
ObjectBidirectionalIterator<Entry<FrameType>> iterator,
ObjectBidirectionalIterator<Entry<FrameType>> otherIterator,
CfFrame.Builder builder) {
- ObjectBidirectionalIterator<Entry<FrameType>> currentIterator = iterator;
- while (currentIterator.hasNext()) {
- Entry<FrameType> entry = nextLocal(currentIterator);
- int currentLocalIndex = entry.getIntKey();
- FrameType currentFrameType = entry.getValue();
-
- // Check if this local overlaps with the previous wide local that was set to top. If not, then
- // this local is not affected.
- if (lastLocalIndexMarkedTop < currentLocalIndex) {
- // The current local still needs to be handled, thus this rewinds the iterator.
- previousLocal(currentIterator);
- break;
- }
-
- // Verify that the low part of the current local has been set to top.
- assert builder.hasLocal(currentLocalIndex);
- assert builder.getLocal(currentLocalIndex).isOneWord();
-
- // If the current local is not a wide, then we're done.
- if (currentFrameType.isSingle()) {
- // The current local has become top due to the overlap with a wide local. Therefore, this
- // intentionally does not rewind the iterator.
- break;
- }
-
- // The current local is a wide. We mark its high local index as top due to the overlap, and
- // check if this wide local overlaps with a wide local in the other locals.
- lastLocalIndexMarkedTop = currentLocalIndex + 1;
- setSingleLocalToTop(lastLocalIndexMarkedTop, builder);
- currentIterator = currentIterator == iterator ? otherIterator : iterator;
+ if (frameType.isWidePrimitiveLow() != otherFrameType.isWidePrimitiveLow()) {
+ setSingleLocalToTop(localIndex, builder);
+ return;
}
+ if (frameType == otherFrameType) {
+ builder.store(localIndex, frameType);
+ } else {
+ setWideLocalToTop(localIndex, builder);
+ }
+ acceptWidePrimitiveHigh(localIndex, frameType, iterator);
+ acceptWidePrimitiveHigh(localIndex, otherFrameType, otherIterator);
+ }
+
+ private void acceptWidePrimitiveHigh(
+ int localIndex,
+ WidePrimitiveFrameType frameType,
+ ObjectBidirectionalIterator<Entry<FrameType>> iterator) {
+ assert iterator.hasNext();
+ Entry<FrameType> entry = iterator.next();
+ int nextLocalIndex = entry.getIntKey();
+ assert nextLocalIndex == localIndex + 1;
+ FrameType nextFrameType = entry.getValue();
+ assert nextFrameType == frameType.getHighType();
}
private void joinLocalsOnlyPresentInOne(
ObjectBidirectionalIterator<Entry<FrameType>> iterator,
CfFrame.Builder builder,
UnaryOperator<FrameType> joinWithMissingLocal) {
+ if (!iterator.hasNext()) {
+ return;
+ }
+ Entry<FrameType> firstEntry = iterator.next();
+ if (firstEntry.getValue().isWidePrimitiveHigh()) {
+ setSingleLocalToTop(firstEntry.getIntKey(), builder);
+ } else {
+ joinLocalOnlyPresentInOne(iterator, firstEntry, builder, joinWithMissingLocal);
+ }
while (iterator.hasNext()) {
- Entry<FrameType> entry = nextLocal(iterator);
- int localIndex = entry.getIntKey();
- FrameType frameType = entry.getValue();
- FrameType joinFrameType = joinWithMissingLocal.apply(frameType);
- assert joinFrameType.isSingle() == frameType.isSingle();
- if (joinFrameType.isOneWord() || joinFrameType.isTwoWord()) {
- setLocalToTop(localIndex, joinFrameType, builder);
- } else {
- builder.store(localIndex, joinFrameType);
- }
+ Entry<FrameType> entry = iterator.next();
+ joinLocalOnlyPresentInOne(iterator, entry, builder, joinWithMissingLocal);
}
}
- private Entry<FrameType> nextLocal(ObjectBidirectionalIterator<Entry<FrameType>> iterator) {
- Entry<FrameType> entry = iterator.next();
+ private void joinLocalOnlyPresentInOne(
+ ObjectBidirectionalIterator<Entry<FrameType>> iterator,
+ Entry<FrameType> entry,
+ CfFrame.Builder builder,
+ UnaryOperator<FrameType> joinWithMissingLocal) {
+ int localIndex = entry.getIntKey();
FrameType frameType = entry.getValue();
- if (frameType.isWidePrimitive()) {
- assert frameType.isDoubleLow() || frameType.isLongLow();
- Entry<FrameType> highEntry = iterator.next();
- assert highEntry.getIntKey() == entry.getIntKey() + 1;
- assert highEntry.getValue() == frameType.asWidePrimitive().getHighType();
- } else {
- assert !frameType.isWide();
+ assert !frameType.isWidePrimitiveHigh();
+ if (frameType.isWidePrimitiveLow()) {
+ acceptWidePrimitiveHigh(localIndex, frameType.asWidePrimitive(), iterator);
}
- return entry;
- }
-
- private void previousLocal(ObjectBidirectionalIterator<Entry<FrameType>> iterator) {
- Entry<FrameType> entry = iterator.previous();
- FrameType frameType = entry.getValue();
- if (frameType.isWidePrimitive()) {
- assert frameType.isDoubleHigh() || frameType.isLongHigh();
- Entry<FrameType> lowEntry = iterator.previous();
- assert lowEntry.getIntKey() == entry.getIntKey() - 1;
- assert lowEntry.getValue() == frameType.asWidePrimitive().getLowType();
+ FrameType joinFrameType = joinWithMissingLocal.apply(frameType);
+ assert joinFrameType.isSingle() == frameType.isSingle();
+ if (joinFrameType.isOneWord() || joinFrameType.isTwoWord()) {
+ setLocalToTop(localIndex, joinFrameType, builder);
} else {
- assert !frameType.isWide();
+ builder.store(localIndex, joinFrameType);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
deleted file mode 100644
index 6e09082..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.optimize.interfaces.analysis;
-
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-
-public class EmptyOpenClosedInterfacesAnalysis extends OpenClosedInterfacesAnalysis {
-
- private static final EmptyOpenClosedInterfacesAnalysis INSTANCE =
- new EmptyOpenClosedInterfacesAnalysis();
-
- private EmptyOpenClosedInterfacesAnalysis() {}
-
- static EmptyOpenClosedInterfacesAnalysis getInstance() {
- return INSTANCE;
- }
-
- @Override
- public void analyze(ProgramMethod method, IRCode code) {
- // Intentionally empty.
- }
-
- @Override
- public void prepareForPrimaryOptimizationPass() {
- // Intentionally empty.
- }
-
- @Override
- public void onPrimaryOptimizationPassComplete() {
- // Intentionally empty.
- }
-}
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 1274958..eb4d25f 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
@@ -11,7 +11,13 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.Pair;
+import java.util.Iterator;
import java.util.function.BiFunction;
/** An analysis state representing that the code does not type check. */
@@ -54,15 +60,30 @@
private static String format(FrameType frameType, FormatKind formatKind) {
if (frameType.isInitialized()) {
- if (frameType.isObject()) {
- DexType initializedType = frameType.asInitializedReferenceType().getInitializedType();
- if (initializedType.isArrayType()) {
- return initializedType.getTypeName();
- } else if (initializedType.isClassType()) {
- return "initialized " + initializedType.getTypeName();
- } else {
- assert initializedType.isNullValueType();
+ if (frameType.isInitializedReferenceType()) {
+ if (frameType.isNullType()) {
return "null";
+ } else if (frameType.isInitializedNonNullReferenceTypeWithInterfaces()) {
+ ReferenceTypeElement initializedType =
+ frameType
+ .asInitializedNonNullReferenceTypeWithInterfaces()
+ .getInitializedTypeWithInterfaces();
+ if (initializedType.isArrayType()) {
+ return format(initializedType);
+ } else {
+ assert initializedType.isClassType();
+ return "initialized " + format(initializedType);
+ }
+ } else {
+ assert frameType.isInitializedNonNullReferenceTypeWithoutInterfaces();
+ DexType initializedType =
+ frameType.asInitializedNonNullReferenceTypeWithoutInterfaces().getInitializedType();
+ if (initializedType.isArrayType()) {
+ return initializedType.getTypeName();
+ } else {
+ assert initializedType.isClassType();
+ return "initialized " + initializedType.getTypeName();
+ }
}
} else {
assert frameType.isPrimitive();
@@ -88,6 +109,45 @@
}
}
+ private static String format(TypeElement type) {
+ if (type.isArrayType()) {
+ ArrayTypeElement arrayType = type.asArrayType();
+ TypeElement baseType = arrayType.getBaseType();
+ assert baseType.isClassType() || baseType.isPrimitiveType();
+ boolean parenthesize =
+ baseType.isClassType() && !baseType.asClassType().getInterfaces().isEmpty();
+ StringBuilder result = new StringBuilder();
+ if (parenthesize) {
+ result.append("(");
+ }
+ result.append(format(baseType));
+ if (parenthesize) {
+ result.append(")");
+ }
+ for (int i = 0; i < arrayType.getNesting(); i++) {
+ result.append("[]");
+ }
+ return result.toString();
+ } else if (type.isClassType()) {
+ ClassTypeElement classType = type.asClassType();
+ StringBuilder result = new StringBuilder(classType.getClassType().getTypeName());
+ if (!classType.getInterfaces().isEmpty()) {
+ Iterator<Pair<DexType, Boolean>> iterator =
+ classType.getInterfaces().getInterfaceList().iterator();
+ result.append(" implements ").append(iterator.next().getFirst().getTypeName());
+ while (iterator.hasNext()) {
+ result.append(", ").append(iterator.next().getFirst().getTypeName());
+ }
+ }
+ return result.toString();
+ } else if (type.isNullType()) {
+ return "null";
+ } else {
+ assert type.isPrimitiveType();
+ return type.asPrimitiveType().getTypeName();
+ }
+ }
+
public static String formatExpected(ValueType valueType) {
return format(valueType);
}
@@ -182,6 +242,11 @@
}
@Override
+ public CfFrameState push(CfAnalysisConfig config, TypeElement type) {
+ return this;
+ }
+
+ @Override
public CfFrameState push(CfAnalysisConfig config, PreciseFrameType frameType) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
deleted file mode 100644
index dc13d50..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
+++ /dev/null
@@ -1,21 +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.optimize.interfaces.analysis;
-
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-
-public abstract class OpenClosedInterfacesAnalysis {
-
- public static EmptyOpenClosedInterfacesAnalysis empty() {
- return EmptyOpenClosedInterfacesAnalysis.getInstance();
- }
-
- public abstract void analyze(ProgramMethod method, IRCode code);
-
- public abstract void prepareForPrimaryOptimizationPass();
-
- public abstract void onPrimaryOptimizationPassComplete();
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
deleted file mode 100644
index 4e474f4..0000000
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
+++ /dev/null
@@ -1,200 +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.optimize.interfaces.analysis;
-
-import static com.android.tools.r8.ir.code.Opcodes.ARRAY_PUT;
-import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.RETURN;
-import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.ArrayPut;
-import com.android.tools.r8.ir.code.FieldPut;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions.OpenClosedInterfacesOptions;
-import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Sets;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-public class OpenClosedInterfacesAnalysisImpl extends OpenClosedInterfacesAnalysis {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final DexItemFactory dexItemFactory;
-
- private Set<DexClass> openInterfaces;
-
- public OpenClosedInterfacesAnalysisImpl(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
- }
-
- @Override
- public void analyze(ProgramMethod method, IRCode code) {
- if (openInterfaces == null) {
- return;
- }
- // Analyze each instruction that may assign to an interface type.
- for (Instruction instruction : code.instructions()) {
- switch (instruction.opcode()) {
- case ARRAY_PUT:
- analyzeArrayPut(instruction.asArrayPut());
- break;
- case INSTANCE_PUT:
- case STATIC_PUT:
- analyzeFieldPut(instruction.asFieldPut());
- break;
- case INVOKE_DIRECT:
- case INVOKE_INTERFACE:
- case INVOKE_STATIC:
- case INVOKE_SUPER:
- case INVOKE_VIRTUAL:
- analyzeInvokeMethod(instruction.asInvokeMethod());
- break;
- case RETURN:
- analyzeReturn(instruction.asReturn(), method);
- break;
- default:
- break;
- }
- }
- }
-
- private void analyzeArrayPut(ArrayPut arrayPut) {
- Value array = arrayPut.array();
- TypeElement arrayType = array.getType();
- if (!arrayType.isArrayType()) {
- return;
- }
- TypeElement valueType = arrayPut.value().getType();
- TypeElement arrayMemberType = arrayType.asArrayType().getMemberType();
- checkAssignment(valueType, arrayMemberType);
- }
-
- private void analyzeFieldPut(FieldPut fieldPut) {
- TypeElement valueType = fieldPut.value().getType();
- TypeElement fieldType = fieldPut.getField().getTypeElement(appView);
- checkAssignment(valueType, fieldType);
- }
-
- private void analyzeInvokeMethod(InvokeMethod invoke) {
- DexTypeList parameters = invoke.getInvokedMethod().getParameters();
- for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) {
- Value argument = invoke.getArgumentForParameter(parameterIndex);
- TypeElement argumentType = argument.getType();
- TypeElement parameterType = parameters.get(parameterIndex).toTypeElement(appView);
- checkAssignment(argumentType, parameterType);
- }
- }
-
- private void analyzeReturn(Return returnInstruction, ProgramMethod context) {
- if (returnInstruction.isReturnVoid()) {
- return;
- }
- TypeElement valueType = returnInstruction.returnValue().getType();
- TypeElement returnType = context.getReturnType().toTypeElement(appView);
- checkAssignment(valueType, returnType);
- }
-
- private void checkAssignment(TypeElement fromType, TypeElement toType) {
- // If the type is an interface type, then check that the assigned value is a subtype of the
- // interface type, or mark the interface as open.
- if (!toType.isClassType()) {
- return;
- }
- ClassTypeElement toClassType = toType.asClassType();
- if (toClassType.getClassType() != dexItemFactory.objectType) {
- return;
- }
- InterfaceCollection interfaceCollection = toClassType.getInterfaces();
- interfaceCollection.forEachKnownInterface(
- knownInterfaceType -> {
- DexClass knownInterface = appView.definitionFor(knownInterfaceType);
- if (knownInterface == null) {
- return;
- }
- assert knownInterface.isInterface();
- if (fromType.lessThanOrEqualUpToNullability(toType, appView)) {
- return;
- }
- assert verifyOpenInterfaceWitnessIsSuppressed(fromType, knownInterface);
- openInterfaces.add(knownInterface);
- });
- }
-
- @Override
- public void prepareForPrimaryOptimizationPass() {
- openInterfaces = Sets.newConcurrentHashSet();
- }
-
- @Override
- public void onPrimaryOptimizationPassComplete() {
- // If open interfaces are not allowed and there are one or more suppressions, we should find at
- // least one open interface.
- OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
- assert options.isOpenInterfacesAllowed()
- || !options.hasSuppressions()
- || !openInterfaces.isEmpty()
- : "Expected to find at least one open interface";
-
- includeParentOpenInterfaces();
- appView.setOpenClosedInterfacesCollection(
- new NonEmptyOpenClosedInterfacesCollection(
- openInterfaces.stream()
- .map(DexClass::getType)
- .collect(
- Collectors.toCollection(
- () -> SetUtils.newIdentityHashSet(openInterfaces.size())))));
- openInterfaces = null;
- }
-
- private void includeParentOpenInterfaces() {
- // This includes all parent interfaces of each open interface in the set of open interfaces,
- // by using the open interfaces as the seen set.
- WorkList<DexClass> worklist = WorkList.newWorkList(openInterfaces);
- worklist.addAllIgnoringSeenSet(openInterfaces);
- while (worklist.hasNext()) {
- DexClass openInterface = worklist.next();
- for (DexType indirectOpenInterfaceType : openInterface.getInterfaces()) {
- DexClass indirectOpenInterfaceDefinition = appView.definitionFor(indirectOpenInterfaceType);
- if (indirectOpenInterfaceDefinition != null) {
- worklist.addIfNotSeen(indirectOpenInterfaceDefinition);
- }
- }
- }
- }
-
- private boolean verifyOpenInterfaceWitnessIsSuppressed(
- TypeElement valueType, DexClass openInterface) {
- OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
- assert options.isSuppressed(appView, valueType, openInterface)
- : "Unexpected open interface "
- + openInterface.getTypeName()
- + " (assignment: "
- + valueType
- + ")";
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
index f8aeb81..df264e4 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
@@ -22,8 +22,7 @@
@Override
public boolean isDefinitelyClosed(DexClass clazz) {
- // TODO(b/214496607): Should return false.
- return true;
+ return false;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.java
new file mode 100644
index 0000000..9b8c0a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitionFromKeySupplier.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;
+
+import com.android.tools.r8.Keep;
+
+/***
+ * Supplier to provide the bytes for a partition specified by a key. Retrace guarantee that the
+ * supplier is always called after a call with the key to {@link RegisterMappingPartitionCallback}
+ * and a call to {@link PrepareMappingPartitionsCallback}
+ *
+ */
+@FunctionalInterface
+@Keep
+public interface MappingPartitionFromKeySupplier {
+
+ byte[] get(String key);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java
deleted file mode 100644
index 56f20b1..0000000
--- a/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java
+++ /dev/null
@@ -1,31 +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.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.TypeReference;
-import java.util.function.Consumer;
-
-@Keep
-public interface MappingPartitionKeyInfo {
-
- void getKeysForClass(ClassReference reference, Consumer<String> keyConsumer);
-
- void getKeysForClassAndMethodName(
- ClassReference reference, String methodName, Consumer<String> keyConsumer);
-
- void getKeysForMethod(MethodReference reference, Consumer<String> keyConsumer);
-
- void getKeysForField(FieldReference fieldReference, Consumer<String> keyConsumer);
-
- void getKeysForType(TypeReference typeReference, Consumer<String> keyConsumer);
-
- static MappingPartitionKeyInfo getDefault(byte[] metadata) {
- return null;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitionMetadata.java
similarity index 74%
rename from src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java
rename to src/main/java/com/android/tools/r8/retrace/MappingPartitionMetadata.java
index 10c641a..32fc36d 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitionMetadata.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.Keep;
@Keep
-public interface MappingPartitioner {
+public interface MappingPartitionMetadata {
- MappingPartitions partition(ProguardMapProducer mapProducer);
+ byte[] getBytes();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
index d5a8f6a..707023d 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplier.java
@@ -18,7 +18,8 @@
*
* @param classReference The minified class reference allowed to be lookup up.
*/
- public abstract T registerClassUse(ClassReference classReference);
+ public abstract T registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference);
public abstract void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
index c57d88a..2a8a646 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
+++ b/src/main/java/com/android/tools/r8/retrace/MappingSupplierBuilder.java
@@ -8,7 +8,7 @@
@Keep
public abstract class MappingSupplierBuilder<
- P extends MappingSupplier, T extends MappingSupplierBuilder<P, T>> {
+ P extends MappingSupplier<P>, T extends MappingSupplierBuilder<P, T>> {
public abstract T self();
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
new file mode 100644
index 0000000..d56aee5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionMappingSupplier.java
@@ -0,0 +1,66 @@
+// 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.naming.MapVersion;
+import com.android.tools.r8.retrace.internal.PartitionMappingSupplierBuilderImpl;
+
+@Keep
+public abstract class PartitionMappingSupplier extends MappingSupplier<PartitionMappingSupplier> {
+
+ public static Builder builder() {
+ return new PartitionMappingSupplierBuilderImpl(MapVersion.MAP_VERSION_NONE);
+ }
+
+ public static NoMetadataBuilder<?> noMetadataBuilder(MapVersion mapVersion) {
+ return new PartitionMappingSupplierBuilderImpl(mapVersion);
+ }
+
+ @Keep
+ public abstract static class NoMetadataBuilder<B extends NoMetadataBuilder<B>>
+ extends MappingSupplierBuilder<PartitionMappingSupplier, B> {
+
+ /***
+ * Callback to be notified of a partition that is later going to be needed. When all needed
+ * partitions are found the callback specified to {@code setPrepareMappingPartitionsCallback} is
+ * called.
+ *
+ * @param registerPartitionCallback the consumer to get keys for partitions.
+ */
+ public abstract Builder setRegisterMappingPartitionCallback(
+ RegisterMappingPartitionCallback registerPartitionCallback);
+
+ /***
+ * A callback notifying that all partitions should be prepared. The prepare callback is
+ * guaranteed to be called prior to any calls to the partition supplier.
+ *
+ * @param prepare the callback to listen for when partitions should be prepared.
+ */
+ public abstract Builder setPrepareMappingPartitionsCallback(
+ PrepareMappingPartitionsCallback prepare);
+
+ /***
+ * Set the partition supplier that is needed for retracing. All partitions needed has been
+ * declared earlier and this should block until the bytes associated with the partition is
+ * ready.
+ *
+ * @param partitionSupplier the function to return a partition to retrace
+ */
+ public abstract Builder setMappingPartitionFromKeySupplier(
+ MappingPartitionFromKeySupplier partitionSupplier);
+ }
+
+ @Keep
+ public abstract static class Builder extends NoMetadataBuilder<Builder> {
+
+ /***
+ * Sets the serialized metadata that was obtained when partitioning.
+ *
+ * @param metadata the serialized metadata
+ */
+ public abstract Builder setMetadata(byte[] metadata);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
new file mode 100644
index 0000000..b19babd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PrepareMappingPartitionsCallback.java
@@ -0,0 +1,19 @@
+// 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;
+
+/***
+ * Callback to be called to prepare all partitions registered by
+ * {@link RegisterMappingPartitionCallback}. This is guaranteed to be called before calling
+ * {@link MappingPartitionFromKeySupplier}.
+ */
+@FunctionalInterface
+@Keep
+public interface PrepareMappingPartitionsCallback {
+
+ void prepare();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitioner.java b/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitioner.java
new file mode 100644
index 0000000..5805ae8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitioner.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;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.ProguardMapPartitionerOnClassNameToText.ProguardMapPartitionerBuilderImpl;
+import java.io.IOException;
+
+@Keep
+public interface ProguardMapPartitioner {
+
+ MappingPartitionMetadata run() throws IOException;
+
+ static ProguardMapPartitionerBuilder<?, ?> builder(DiagnosticsHandler diagnosticsHandler) {
+ return new ProguardMapPartitionerBuilderImpl(diagnosticsHandler);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitionerBuilder.java b/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitionerBuilder.java
new file mode 100644
index 0000000..42fd546
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMapPartitionerBuilder.java
@@ -0,0 +1,19 @@
+// 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 java.util.function.Consumer;
+
+@Keep
+public interface ProguardMapPartitionerBuilder<
+ B extends ProguardMapPartitionerBuilder<B, P>, P extends ProguardMapPartitioner> {
+
+ B setProguardMapProducer(ProguardMapProducer proguardMapProducer);
+
+ B setPartitionConsumer(Consumer<MappingPartition> consumer);
+
+ P build();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitions.java b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
similarity index 62%
rename from src/main/java/com/android/tools/r8/retrace/MappingPartitions.java
rename to src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
index 9bb9eb7..df5c943 100644
--- a/src/main/java/com/android/tools/r8/retrace/MappingPartitions.java
+++ b/src/main/java/com/android/tools/r8/retrace/RegisterMappingPartitionCallback.java
@@ -5,12 +5,13 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import java.util.function.Consumer;
+/***
+ * Interface for registering a mapping partition to be used later.
+ */
+@FunctionalInterface
@Keep
-public interface MappingPartitions {
+public interface RegisterMappingPartitionCallback {
- byte[] getMetadata();
-
- void visitPartitions(Consumer<MappingPartition> consumer);
+ void register(String key);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java b/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
index 03988a9..4949aec 100644
--- a/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
+++ b/src/main/java/com/android/tools/r8/retrace/ResultWithContext.java
@@ -5,11 +5,17 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
+import java.util.List;
+import java.util.function.Consumer;
@Keep
public interface ResultWithContext<T> {
RetraceStackTraceContext getContext();
- T getResult();
+ List<T> getLines();
+
+ void forEach(Consumer<T> consumer);
+
+ boolean isEmpty();
}
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 bb1bd0e..29b68ab 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -181,7 +181,7 @@
* @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public ResultWithContext<List<List<List<T>>>> retraceStackTrace(
+ public ResultWithContext<List<List<T>>> retraceStackTrace(
List<T> stackTrace, RetraceStackTraceContext context) {
ListUtils.forEachWithIndex(
stackTrace,
@@ -203,7 +203,7 @@
* @param context The context to retrace the stack trace in
* @return list of potentially ambiguous stack traces.
*/
- public ResultWithContext<List<List<List<T>>>> retraceStackTraceParsed(
+ public ResultWithContext<List<List<T>>> retraceStackTraceParsed(
List<ST> stackTrace, RetraceStackTraceContext context) {
RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
@@ -255,7 +255,7 @@
* @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 ResultWithContext<List<List<T>>> retraceFrame(
+ public ResultWithContext<List<T>> retraceFrame(
T stackTraceFrame, RetraceStackTraceContext context) {
Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
@@ -287,8 +287,7 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace line
*/
- public ResultWithContext<List<T>> retraceLine(
- T stackTraceLine, RetraceStackTraceContext context) {
+ public ResultWithContext<T> retraceLine(T stackTraceLine, RetraceStackTraceContext context) {
ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
Box<RetraceStackTraceContext> contextBox = new Box<>(context);
List<T> result =
@@ -361,25 +360,27 @@
parsedStackTrace.forEach(
proxy -> {
if (proxy.hasClassName()) {
- mappingSupplier.registerClassUse(proxy.getClassReference());
+ mappingSupplier.registerClassUse(diagnosticsHandler, proxy.getClassReference());
}
if (proxy.hasMethodArguments()) {
Arrays.stream(proxy.getMethodArguments().split(","))
- .forEach(typeName -> registerUseFromTypeReference(mappingSupplier, typeName));
+ .forEach(
+ typeName ->
+ registerUseFromTypeReference(
+ mappingSupplier, typeName, diagnosticsHandler));
}
if (proxy.hasFieldOrReturnType() && !proxy.getFieldOrReturnType().equals("void")) {
- registerUseFromTypeReference(mappingSupplier, proxy.getFieldOrReturnType());
+ registerUseFromTypeReference(
+ mappingSupplier, proxy.getFieldOrReturnType(), diagnosticsHandler);
}
});
timing.begin("Retracing");
- ResultWithContext<List<String>> listResultWithContext =
- stringRetracer.retraceParsed(parsedStackTrace, context);
+ ResultWithContext<String> result = stringRetracer.retraceParsed(parsedStackTrace, context);
timing.end();
timing.begin("Report result");
- context = listResultWithContext.getContext();
- List<String> result = listResultWithContext.getResult();
+ context = result.getContext();
if (!result.isEmpty() || currentStackTrace.isEmpty()) {
- command.getRetracedStackTraceConsumer().accept(result);
+ command.getRetracedStackTraceConsumer().accept(result.getLines());
}
timing.end();
}
@@ -393,13 +394,13 @@
}
private static void registerUseFromTypeReference(
- MappingSupplier<?> mappingSupplier, String typeName) {
+ MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
TypeReference typeReference = Reference.typeFromTypeName(typeName);
if (typeReference.isArray()) {
typeReference = typeReference.asArray().getBaseType();
}
if (typeReference.isClass()) {
- mappingSupplier.registerClassUse(typeReference.asClass());
+ mappingSupplier.registerClassUse(diagnosticsHandler, typeReference.asClass());
}
}
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 7c00ed2..513b39f 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
@@ -20,12 +20,12 @@
private final boolean verifyMappingFileHash;
private final String regularExpression;
private final DiagnosticsHandler diagnosticsHandler;
- private final MappingSupplier mappingSupplier;
+ private final MappingSupplier<?> mappingSupplier;
private RetraceOptions(
String regularExpression,
DiagnosticsHandler diagnosticsHandler,
- MappingSupplier mappingSupplier,
+ MappingSupplier<?> mappingSupplier,
boolean isVerbose,
boolean verifyMappingFileHash) {
this.regularExpression = regularExpression;
@@ -54,7 +54,7 @@
return diagnosticsHandler;
}
- public MappingSupplier getMappingSupplier() {
+ public MappingSupplier<?> getMappingSupplier() {
return mappingSupplier;
}
@@ -78,7 +78,7 @@
private boolean isVerbose;
private boolean verifyMappingFileHash;
private final DiagnosticsHandler diagnosticsHandler;
- private MappingSupplier mappingSupplier;
+ private MappingSupplier<?> mappingSupplier;
private String regularExpression = defaultRegularExpression();
Builder(DiagnosticsHandler diagnosticsHandler) {
@@ -98,11 +98,17 @@
}
/** Set a mapping supplier for providing mapping contents. */
- public Builder setMappingSupplier(MappingSupplier producer) {
- this.mappingSupplier = producer;
+ public Builder setMappingSupplier(MappingSupplier<?> supplier) {
+ this.mappingSupplier = supplier;
return this;
}
+ /** Helper method to set a ProguardMapSupplier from a ProguardMapProducer */
+ public Builder setProguardMapProducer(ProguardMapProducer producer) {
+ return setMappingSupplier(
+ ProguardMappingSupplier.builder().setProguardMapProducer(producer).build());
+ }
+
/**
* Set a regular expression for parsing the incoming text. The Regular expression must not use
* naming groups and has special wild cards according to proguard retrace. Note, this will
diff --git a/src/main/java/com/android/tools/r8/retrace/StreamSupplier.java b/src/main/java/com/android/tools/r8/retrace/StreamSupplier.java
new file mode 100644
index 0000000..4a51497
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StreamSupplier.java
@@ -0,0 +1,19 @@
+// 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;
+
+/**
+ * Supplier of input lines to be retraced.
+ *
+ * @param <E> the type of the {@link Throwable}
+ */
+@FunctionalInterface
+@Keep
+public interface StreamSupplier<E extends Throwable> {
+
+ String getNext() throws E;
+}
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 09f30ca..164a208 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -18,7 +18,6 @@
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Specialized Retrace class for retracing string retraces, with special handling for appending
@@ -82,12 +81,12 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public ResultWithContext<List<String>> retrace(
+ public ResultWithContext<String> retrace(
List<String> stackTrace, RetraceStackTraceContext context) {
- ResultWithContext<List<List<List<String>>>> listResultWithContext =
+ ResultWithContext<List<List<String>>> listResultWithContext =
retraceStackTrace(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- for (List<List<String>> newLines : listResultWithContext.getResult()) {
+ for (List<List<String>> newLines : listResultWithContext.getLines()) {
ListUtils.forEachWithIndex(
newLines,
(inlineFrames, ambiguousIndex) -> {
@@ -122,12 +121,12 @@
* @param context The context to retrace the stack trace in
* @return the retraced stack trace
*/
- public ResultWithContext<List<String>> retraceParsed(
+ public ResultWithContext<String> retraceParsed(
List<StackTraceElementStringProxy> stackTrace, RetraceStackTraceContext context) {
- ResultWithContext<List<List<List<String>>>> listResultWithContext =
+ ResultWithContext<List<List<String>>> listResultWithContext =
retraceStackTraceParsed(stackTrace, context);
List<String> retracedStrings = new ArrayList<>();
- for (List<List<String>> newLines : listResultWithContext.getResult()) {
+ for (List<List<String>> newLines : listResultWithContext.getLines()) {
ListUtils.forEachWithIndex(
newLines,
(inlineFrames, ambiguousIndex) -> {
@@ -161,12 +160,11 @@
* @param context The context to retrace the stack trace in
* @return the retraced frames
*/
- public ResultWithContext<List<String>> retrace(
+ public ResultWithContext<String> retrace(
String stackTraceLine, RetraceStackTraceContext context) {
- ResultWithContext<List<List<String>>> listResultWithContext =
- retraceFrame(stackTraceLine, context);
+ ResultWithContext<List<String>> listResultWithContext = retraceFrame(stackTraceLine, context);
List<String> result = new ArrayList<>();
- joinAmbiguousLines(listResultWithContext.getResult(), result::add);
+ joinAmbiguousLines(listResultWithContext.getLines(), result::add);
return ResultWithContextImpl.create(result, listResultWithContext.getContext());
}
@@ -176,13 +174,14 @@
* @param lineSupplier the supplier of strings with returning null as terminator
* @param lineConsumer the consumer of retraced strings
*/
- public void retrace(Supplier<String> lineSupplier, Consumer<String> lineConsumer) {
+ public <E extends Throwable> void retraceSupplier(
+ StreamSupplier<E> lineSupplier, Consumer<String> lineConsumer) throws E {
RetraceStackTraceContext context = RetraceStackTraceContext.empty();
String retraceLine;
- while ((retraceLine = lineSupplier.get()) != null) {
- ResultWithContext<List<String>> result = retrace(retraceLine, context);
+ while ((retraceLine = lineSupplier.getNext()) != null) {
+ ResultWithContext<String> result = retrace(retraceLine, context);
context = result.getContext();
- result.getResult().forEach(lineConsumer);
+ result.forEach(lineConsumer);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionImpl.java
new file mode 100644
index 0000000..4a12147
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionImpl.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import com.android.tools.r8.retrace.MappingPartition;
+
+public class MappingPartitionImpl implements MappingPartition {
+
+ private final String key;
+ private final byte[] payload;
+
+ public MappingPartitionImpl(String key, byte[] payload) {
+ this.key = key;
+ this.payload = payload;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
new file mode 100644
index 0000000..ad53968
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.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 com.android.tools.r8.retrace.internal;
+
+public enum MappingPartitionKeyStrategy {
+ OBFUSCATED_TYPE_NAME_AS_KEY(0);
+
+ final int serializedKey;
+
+ MappingPartitionKeyStrategy(int serializedKey) {
+ this.serializedKey = serializedKey;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
new file mode 100644
index 0000000..6c2e3ca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.internal;
+
+import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.google.common.primitives.Ints;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+public interface MappingPartitionMetadataInternal extends MappingPartitionMetadata {
+
+ String getKey(ClassReference classReference);
+
+ MapVersion getMapVersion();
+
+ byte ZERO_BYTE = (byte) 0;
+
+ static MappingPartitionMetadataInternal createFromBytes(
+ byte[] bytes, MapVersion fallBackMapVersion, DiagnosticsHandler diagnosticsHandler) {
+ if (bytes == null) {
+ return obfuscatedTypeNameAsKey(fallBackMapVersion);
+ } else if (bytes.length > 2) {
+ int serializedStrategyId = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[0], bytes[1]);
+ MapVersion mapVersion = MapVersion.fromName(new String(bytes, 2, bytes.length - 2));
+ if (serializedStrategyId == OBFUSCATED_TYPE_NAME_AS_KEY.serializedKey) {
+ return obfuscatedTypeNameAsKey(mapVersion);
+ }
+ }
+ RuntimeException exception = new RuntimeException("Unable to build key strategy from metadata");
+ diagnosticsHandler.error(new ExceptionDiagnostic(exception));
+ throw exception;
+ }
+
+ static ObfuscatedTypeNameAsKeyMetadata obfuscatedTypeNameAsKey(MapVersion mapVersion) {
+ return new ObfuscatedTypeNameAsKeyMetadata(mapVersion);
+ }
+
+ class ObfuscatedTypeNameAsKeyMetadata implements MappingPartitionMetadataInternal {
+
+ private final MapVersion mapVersion;
+
+ private ObfuscatedTypeNameAsKeyMetadata(MapVersion mapVersion) {
+ this.mapVersion = mapVersion;
+ }
+
+ @Override
+ public String getKey(ClassReference classReference) {
+ return classReference.getTypeName();
+ }
+
+ @Override
+ public MapVersion getMapVersion() {
+ return mapVersion;
+ }
+
+ @Override
+ public byte[] getBytes() {
+ try {
+ ByteArrayOutputStream temp = new ByteArrayOutputStream();
+ DataOutputStream dataOutputStream = new DataOutputStream(temp);
+ dataOutputStream.writeShort(OBFUSCATED_TYPE_NAME_AS_KEY.serializedKey);
+ dataOutputStream.writeBytes(mapVersion.getName());
+ dataOutputStream.close();
+ return temp.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.java
new file mode 100644
index 0000000..2f69645
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBuilderImpl.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.retrace.internal;
+
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
+import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
+
+public class PartitionMappingSupplierBuilderImpl extends PartitionMappingSupplier.Builder {
+
+ private MappingPartitionFromKeySupplier partitionSupplier;
+ private RegisterMappingPartitionCallback registerPartitionCallback = key -> {};
+ private PrepareMappingPartitionsCallback prepare = () -> {};
+ private byte[] metadata;
+ private final MapVersion fallbackMapVersion;
+ private boolean allowExperimental = false;
+
+ public PartitionMappingSupplierBuilderImpl(MapVersion fallbackMapVersion) {
+ this.fallbackMapVersion = fallbackMapVersion;
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder self() {
+ return this;
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder setAllowExperimental(boolean allowExperimental) {
+ this.allowExperimental = allowExperimental;
+ return self();
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder setMetadata(byte[] metadata) {
+ this.metadata = metadata;
+ return self();
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder setRegisterMappingPartitionCallback(
+ RegisterMappingPartitionCallback registerPartitionCallback) {
+ this.registerPartitionCallback = registerPartitionCallback;
+ return self();
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder setPrepareMappingPartitionsCallback(
+ PrepareMappingPartitionsCallback prepare) {
+ this.prepare = prepare;
+ return self();
+ }
+
+ @Override
+ public PartitionMappingSupplier.Builder setMappingPartitionFromKeySupplier(
+ MappingPartitionFromKeySupplier partitionSupplier) {
+ this.partitionSupplier = partitionSupplier;
+ return self();
+ }
+
+ @Override
+ public PartitionMappingSupplier build() {
+ if (partitionSupplier == null) {
+ throw new RuntimeException(
+ "Cannot build without providing a mapping partition from key supplier.");
+ }
+ return new PartitionMappingSupplierImpl(
+ metadata,
+ registerPartitionCallback,
+ prepare,
+ partitionSupplier,
+ allowExperimental,
+ fallbackMapVersion);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
new file mode 100644
index 0000000..dfddd37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
@@ -0,0 +1,138 @@
+// 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 static com.google.common.base.Predicates.alwaysTrue;
+
+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.mappinginformation.MapVersionMappingInformation;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.InvalidMappingFileException;
+import com.android.tools.r8.retrace.MappingPartitionFromKeySupplier;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
+import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+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 PartitionMappingSupplierImpl extends PartitionMappingSupplier {
+
+ private final byte[] metadata;
+ private final RegisterMappingPartitionCallback registerPartitionCallback;
+ private final PrepareMappingPartitionsCallback prepare;
+ private final MappingPartitionFromKeySupplier partitionSupplier;
+ private final boolean allowExperimental;
+ private final MapVersion fallbackMapVersion;
+
+ private ClassNameMapper classNameMapper;
+ private final Set<String> pendingKeys = new LinkedHashSet<>();
+ private final Set<String> builtKeys = new HashSet<>();
+
+ private MappingPartitionMetadataInternal mappingPartitionMetadataCache;
+
+ PartitionMappingSupplierImpl(
+ byte[] metadata,
+ RegisterMappingPartitionCallback registerPartitionCallback,
+ PrepareMappingPartitionsCallback prepare,
+ MappingPartitionFromKeySupplier partitionSupplier,
+ boolean allowExperimental,
+ MapVersion fallbackMapVersion) {
+ this.metadata = metadata;
+ this.registerPartitionCallback = registerPartitionCallback;
+ this.prepare = prepare;
+ this.partitionSupplier = partitionSupplier;
+ this.allowExperimental = allowExperimental;
+ this.fallbackMapVersion = fallbackMapVersion;
+ }
+
+ private MappingPartitionMetadataInternal getMetadata(DiagnosticsHandler diagnosticsHandler) {
+ if (mappingPartitionMetadataCache != null) {
+ return mappingPartitionMetadataCache;
+ }
+ return mappingPartitionMetadataCache =
+ MappingPartitionMetadataInternal.createFromBytes(
+ metadata, fallbackMapVersion, diagnosticsHandler);
+ }
+
+ @Override
+ Set<MapVersionMappingInformation> getMapVersions(DiagnosticsHandler diagnosticsHandler) {
+ return Collections.singleton(
+ new MapVersionMappingInformation(getMetadata(diagnosticsHandler).getMapVersion(), ""));
+ }
+
+ @Override
+ ClassNamingForNameMapper getClassNaming(DiagnosticsHandler diagnosticsHandler, String typeName) {
+ registerClassUse(diagnosticsHandler, Reference.classFromTypeName(typeName));
+ return getClassNameMapper(diagnosticsHandler).getClassNaming(typeName);
+ }
+
+ @Override
+ String getSourceFileForClass(DiagnosticsHandler diagnosticsHandler, String typeName) {
+ // Getting source file should not trigger new fetches of partitions so we are not calling
+ // register here.
+ return getClassNameMapper(diagnosticsHandler).getSourceFile(typeName);
+ }
+
+ private ClassNameMapper getClassNameMapper(DiagnosticsHandler diagnosticsHandler) {
+ MappingPartitionMetadataInternal metadata = getMetadata(diagnosticsHandler);
+ if (!pendingKeys.isEmpty()) {
+ prepare.prepare();
+ }
+ for (String pendingKey : pendingKeys) {
+ try {
+ LineReader reader =
+ new ProguardMapReaderWithFilteringInputBuffer(
+ new ByteArrayInputStream(partitionSupplier.get(pendingKey)), alwaysTrue(), true);
+ classNameMapper =
+ ClassNameMapper.mapperFromLineReaderWithFiltering(
+ reader, metadata.getMapVersion(), diagnosticsHandler, true, allowExperimental)
+ .combine(this.classNameMapper);
+ } catch (IOException e) {
+ throw new InvalidMappingFileException(e);
+ }
+ }
+ builtKeys.addAll(pendingKeys);
+ pendingKeys.clear();
+ if (classNameMapper == null) {
+ classNameMapper = ClassNameMapper.builder().build();
+ }
+ return classNameMapper;
+ }
+
+ @Override
+ public PartitionMappingSupplier registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
+ registerKeyUse(getMetadata(diagnosticsHandler).getKey(classReference));
+ return this;
+ }
+
+ private void registerKeyUse(String key) {
+ if (!builtKeys.contains(key) && pendingKeys.add(key)) {
+ registerPartitionCallback.register(key);
+ }
+ }
+
+ @Override
+ public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
+ String errorMessage = "Cannot verify map file hash for partitions";
+ diagnosticsHandler.error(new StringDiagnostic(errorMessage));
+ throw new RuntimeException(errorMessage);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
new file mode 100644
index 0000000..9770a0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
@@ -0,0 +1,189 @@
+// 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 static com.google.common.base.Predicates.alwaysTrue;
+
+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.mappinginformation.FileNameInformation;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapPartitionerBuilder;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+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.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class ProguardMapPartitionerOnClassNameToText implements ProguardMapPartitioner {
+
+ private final ProguardMapProducer proguardMapProducer;
+ private final Consumer<MappingPartition> mappingPartitionConsumer;
+ private final DiagnosticsHandler diagnosticsHandler;
+
+ private ProguardMapPartitionerOnClassNameToText(
+ ProguardMapProducer proguardMapProducer,
+ Consumer<MappingPartition> mappingPartitionConsumer,
+ DiagnosticsHandler diagnosticsHandler) {
+ this.proguardMapProducer = proguardMapProducer;
+ this.mappingPartitionConsumer = mappingPartitionConsumer;
+ this.diagnosticsHandler = diagnosticsHandler;
+ }
+
+ @Override
+ public MappingPartitionMetadata run() throws IOException {
+ PartitionLineReader reader =
+ new PartitionLineReader(
+ proguardMapProducer.isFileBacked()
+ ? new ProguardMapReaderWithFilteringMappedBuffer(
+ proguardMapProducer.getPath(), alwaysTrue(), true)
+ : new ProguardMapReaderWithFilteringInputBuffer(
+ proguardMapProducer.get(), alwaysTrue(), true));
+ // Produce a global mapper to read all from the reader but also to capture all source file
+ // mappings.
+ ClassNameMapper classMapper =
+ ClassNameMapper.mapperFromLineReaderWithFiltering(
+ reader, MapVersion.MAP_VERSION_UNKNOWN, diagnosticsHandler, true, true);
+ // We can then iterate over all sections.
+ reader.forEachClassMapping(
+ (classMapping, entries) -> {
+ try {
+ String payload = StringUtils.join("\n", entries);
+ ClassNameMapper classNameMapper = ClassNameMapper.mapperFromString(payload);
+ if (classNameMapper.getClassNameMappings().size() != 1) {
+ diagnosticsHandler.error(
+ new StringDiagnostic("Multiple class names in payload\n: " + payload));
+ return;
+ }
+ Entry<String, ClassNamingForNameMapper> currentClassMapping =
+ classNameMapper.getClassNameMappings().entrySet().iterator().next();
+ ClassNamingForNameMapper value = currentClassMapping.getValue();
+ Set<String> seenMappings = new HashSet<>();
+ StringBuilder payloadWithClassReferences = new StringBuilder();
+ value.visitAllFullyQualifiedReferences(
+ holder -> {
+ if (seenMappings.add(holder)) {
+ payloadWithClassReferences.append(
+ getSourceFileMapping(holder, classMapper.getSourceFile(holder)));
+ }
+ });
+ payloadWithClassReferences.append(payload);
+ mappingPartitionConsumer.accept(
+ new MappingPartitionImpl(
+ currentClassMapping.getKey(),
+ payloadWithClassReferences.toString().getBytes(StandardCharsets.UTF_8)));
+ } catch (IOException e) {
+ diagnosticsHandler.error(new ExceptionDiagnostic(e));
+ }
+ });
+ MapVersion mapVersion = MapVersion.MAP_VERSION_UNKNOWN;
+ MapVersionMappingInformation mapVersionInfo = classMapper.getFirstMapVersionInformation();
+ if (mapVersionInfo != null) {
+ mapVersion = mapVersionInfo.getMapVersion();
+ }
+ return MappingPartitionMetadataInternal.obfuscatedTypeNameAsKey(mapVersion);
+ }
+
+ private String getSourceFileMapping(String className, String sourceFile) {
+ if (sourceFile == null) {
+ return "";
+ }
+ return className
+ + " -> "
+ + className
+ + ":"
+ + "\n # "
+ + FileNameInformation.build(sourceFile).serialize()
+ + "\n";
+ }
+
+ public static class PartitionLineReader implements LineReader {
+
+ private final ProguardMapReaderWithFiltering lineReader;
+ private final Map<String, List<String>> readSections = new LinkedHashMap<>();
+ private final List<String> preamble;
+ private List<String> currentList;
+
+ public PartitionLineReader(ProguardMapReaderWithFiltering lineReader) {
+ this.lineReader = lineReader;
+ currentList = new ArrayList<>();
+ preamble = currentList;
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ String readLine = lineReader.readLine();
+ if (readLine == null) {
+ return null;
+ }
+ if (lineReader.isClassMapping()) {
+ currentList = new ArrayList<>();
+ readSections.put(readLine, currentList);
+ }
+ currentList.add(readLine);
+ return readLine;
+ }
+
+ @Override
+ public void close() throws IOException {
+ lineReader.close();
+ }
+
+ public void forEachClassMapping(BiConsumer<String, List<String>> consumer) {
+ readSections.forEach(consumer);
+ }
+ }
+
+ public static class ProguardMapPartitionerBuilderImpl
+ implements ProguardMapPartitionerBuilder<
+ ProguardMapPartitionerBuilderImpl, ProguardMapPartitionerOnClassNameToText> {
+
+ private ProguardMapProducer proguardMapProducer;
+ private Consumer<MappingPartition> mappingPartitionConsumer;
+ private final DiagnosticsHandler diagnosticsHandler;
+
+ public ProguardMapPartitionerBuilderImpl(DiagnosticsHandler diagnosticsHandler) {
+ this.diagnosticsHandler = diagnosticsHandler;
+ }
+
+ @Override
+ public ProguardMapPartitionerBuilderImpl setPartitionConsumer(
+ Consumer<MappingPartition> consumer) {
+ this.mappingPartitionConsumer = consumer;
+ return this;
+ }
+
+ @Override
+ public ProguardMapPartitionerBuilderImpl setProguardMapProducer(
+ ProguardMapProducer proguardMapProducer) {
+ this.proguardMapProducer = proguardMapProducer;
+ return this;
+ }
+
+ @Override
+ public ProguardMapPartitionerOnClassNameToText build() {
+ return new ProguardMapPartitionerOnClassNameToText(
+ proguardMapProducer, mappingPartitionConsumer, 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 e6589d5f..7c30dcd 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
@@ -6,6 +6,7 @@
import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.COMPLETE_CLASS_MAPPING;
import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.IS_COMMENT_SOURCE_FILE;
+import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
import static java.lang.Integer.MAX_VALUE;
import com.android.tools.r8.errors.Unreachable;
@@ -207,6 +208,7 @@
private boolean isInsideClassOfInterest = false;
private boolean seenFirstClass = false;
+ private LineParserState lineParserState = NOT_CLASS_MAPPING_OR_SOURCE_FILE;
@Override
public String readLine() throws IOException {
@@ -218,7 +220,7 @@
if (filter == null) {
return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
}
- LineParserState lineParserState = LineParserState.computeState(bytes, startIndex, endIndex);
+ lineParserState = LineParserState.computeState(bytes, startIndex, endIndex);
if (lineParserState == COMPLETE_CLASS_MAPPING) {
seenFirstClass = true;
String classMapping = getBufferAsString(bytes);
@@ -235,6 +237,10 @@
}
}
+ public boolean isClassMapping() {
+ return lineParserState == COMPLETE_CLASS_MAPPING;
+ }
+
private String getBufferAsString(byte[] bytes) {
return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
}
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
index a6ae27b..632ca66 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
@@ -112,13 +112,14 @@
if (classNameMapper == null) {
return MapVersion.MAP_VERSION_NONE;
} else {
- MapVersionMappingInformation mapVersion = classNameMapper.getFirstMappingInformation();
+ MapVersionMappingInformation mapVersion = classNameMapper.getFirstMapVersionInformation();
return mapVersion == null ? MapVersion.MAP_VERSION_UNKNOWN : mapVersion.getMapVersion();
}
}
@Override
- public ProguardMappingSupplier registerClassUse(ClassReference classReference) {
+ public ProguardMappingSupplier registerClassUse(
+ DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
if (!hasClassMappingFor(classReference.getTypeName())) {
pendingClassMappings.add(classReference.getTypeName());
}
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
index ab92a65..e65ecd5 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ResultWithContextImpl.java
@@ -6,18 +6,20 @@
import com.android.tools.r8.retrace.ResultWithContext;
import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import java.util.List;
+import java.util.function.Consumer;
public class ResultWithContextImpl<T> implements ResultWithContext<T> {
- private final T result;
+ private final List<T> result;
private final RetraceStackTraceContext context;
- private ResultWithContextImpl(T result, RetraceStackTraceContext context) {
+ private ResultWithContextImpl(List<T> result, RetraceStackTraceContext context) {
this.result = result;
this.context = context;
}
- public static <T> ResultWithContext<T> create(T result, RetraceStackTraceContext context) {
+ public static <T> ResultWithContext<T> create(List<T> result, RetraceStackTraceContext context) {
return new ResultWithContextImpl<>(result, context);
}
@@ -27,7 +29,17 @@
}
@Override
- public T getResult() {
+ public List<T> getLines() {
return result;
}
+
+ @Override
+ public void forEach(Consumer<T> consumer) {
+ result.forEach(consumer);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return result.isEmpty();
+ }
}
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 689a3f0..6214a5a 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5387,10 +5387,16 @@
fieldReference, new FieldAccessInfoImpl(fieldReference));
fieldAccessInfo.setReadFromAnnotation();
markFieldAsLive(field, context, reason);
- // When an annotation has a field of an enum type the JVM will use the values() method on
- // that enum class if the field is referenced.
+ // In a class file an enum reference in an annotation is written as enum descriptor and
+ // enum name. At runtime the JVM use valueOf on the enum class with the name to get the
+ // instance. This indirectly use the values() method on that enum class. Also keep the
+ // name of the field and the name of the enum in sync as otherwise recovering the field to
+ // name relationship requires analysis of the enum <clinit> when this CF code is processed
+ // again (e.g. as input to D8 for converting to DEX). See b/236691999 for more info.
if (options.isGeneratingClassFiles() && field.getHolder().isEnum()) {
markEnumValuesAsReachable(field.getHolder(), reason);
+ applyMinimumKeepInfoWhenLive(
+ field, KeepFieldInfo.newEmptyJoiner().disallowMinification());
}
} else {
// There is no dispatch on annotations, so only keep what is directly referenced.
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 1932bd0..fe4baac 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -205,14 +205,22 @@
assert !synthesizingContextReferences.isEmpty();
for (DexReference synthesizingContextReference : synthesizingContextReferences) {
if (synthesizingContextReference.isDexMethod()) {
- DexProgramClass holder =
+ DexProgramClass synthesizingContextHolder =
appView
.definitionFor(synthesizingContextReference.getContextType())
.asProgramClass();
ProgramMethod synthesizingContext =
- holder.lookupProgramMethod(synthesizingContextReference.asDexMethod());
- assert synthesizingContext != null;
- rewrittenContexts.add(synthesizingContext);
+ synthesizingContextHolder.lookupProgramMethod(
+ synthesizingContextReference.asDexMethod());
+ if (synthesizingContext != null) {
+ rewrittenContexts.add(synthesizingContext);
+ } else {
+ // The synthesizing context no longer exists. It must have been forcefully moved due
+ // to desugaring. For now we report the holder of the synthesizing context as the
+ // origin of the missing class reference.
+ assert synthesizingContextHolder.isInterface();
+ rewrittenContexts.add(synthesizingContextHolder);
+ }
} else if (synthesizingContextReference.isDexType()) {
DexProgramClass synthesizingClass =
appView.definitionFor(synthesizingContextReference.asDexType()).asProgramClass();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index eb764c0..822fa38 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -744,8 +744,6 @@
}
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(externalType);
if (clazz != null && isNotSyntheticType(clazz.type)) {
- assert options.testing.allowConflictingSyntheticTypes
- : "Unexpected creation of an existing external synthetic type: " + clazz;
externalType = null;
}
} while (externalType == null);
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
new file mode 100644
index 0000000..b2c4269
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
@@ -0,0 +1,202 @@
+// 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.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.OutputMode;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Wrapper to make it easy to call D8 mode when compiling a dump file.
+ *
+ * <p>This wrapper will be added to the classpath so it *must* only refer to the public API. See
+ * {@code tools/compiledump.py}.
+ *
+ * <p>It is tempting to have this class share the D8 parser code, but such refactoring would not be
+ * valid on past version of the D8 API. Thus there is little else to do than reimplement the parts
+ * we want to support for reading dumps.
+ */
+public class CompileDumpD8 {
+
+ private static final List<String> VALID_OPTIONS =
+ Arrays.asList(
+ "--classfile",
+ "--debug",
+ "--release",
+ "--enable-missing-library-api-modeling",
+ "--android-platform-build");
+
+ private static final List<String> VALID_OPTIONS_WITH_SINGLE_OPERAND =
+ Arrays.asList(
+ "--output",
+ "--lib",
+ "--classpath",
+ "--min-api",
+ "--main-dex-rules",
+ "--main-dex-list",
+ "--main-dex-list-output",
+ "--desugared-lib",
+ "--threads");
+
+ public static void main(String[] args) throws CompilationFailedException {
+ OutputMode outputMode = OutputMode.DexIndexed;
+ Path outputPath = null;
+ Path desugaredLibJson = null;
+ CompilationMode compilationMode = CompilationMode.RELEASE;
+ List<Path> program = new ArrayList<>();
+ List<Path> library = new ArrayList<>();
+ List<Path> classpath = new ArrayList<>();
+ 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)) {
+ switch (option) {
+ case "--classfile":
+ {
+ outputMode = OutputMode.ClassFile;
+ break;
+ }
+ case "--debug":
+ {
+ compilationMode = CompilationMode.DEBUG;
+ break;
+ }
+ case "--release":
+ {
+ 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);
+ }
+ } else if (VALID_OPTIONS_WITH_SINGLE_OPERAND.contains(option)) {
+ String operand = args[++i];
+ switch (option) {
+ case "--output":
+ {
+ outputPath = Paths.get(operand);
+ break;
+ }
+ case "--lib":
+ {
+ library.add(Paths.get(operand));
+ break;
+ }
+ case "--classpath":
+ {
+ classpath.add(Paths.get(operand));
+ break;
+ }
+ case "--min-api":
+ {
+ minApi = Integer.parseInt(operand);
+ break;
+ }
+ case "--desugared-lib":
+ {
+ desugaredLibJson = Paths.get(operand);
+ break;
+ }
+ case "--threads":
+ {
+ threads = Integer.parseInt(operand);
+ break;
+ }
+ case "--main-dex-rules":
+ {
+ mainDexRulesFiles.add(Paths.get(operand));
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unimplemented option: " + option);
+ }
+ } else {
+ program.add(Paths.get(option));
+ }
+ }
+ D8Command.Builder commandBuilder =
+ D8Command.builder()
+ .addProgramFiles(program)
+ .addLibraryFiles(library)
+ .addClasspathFiles(classpath)
+ .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));
+ }
+ commandBuilder.setMinApiLevel(minApi);
+ D8Command command = commandBuilder.build();
+ if (threads != -1) {
+ ExecutorService executor = Executors.newWorkStealingPool(threads);
+ try {
+ D8.run(command, executor);
+ } finally {
+ executor.shutdown();
+ }
+ } else {
+ D8.run(command);
+ }
+ }
+
+ private static Consumer<Object[]> getReflectiveBuilderMethod(
+ D8Command.Builder builder, String setter, Class<?>... parameters) {
+ try {
+ Method declaredMethod = D8Command.Builder.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) {
+ String content = "";
+
+ try {
+ content = new String(Files.readAllBytes(filePath));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return content;
+ }
+}
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 2612127..d338a11 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1621,6 +1621,14 @@
allowOpenInterfaces = false;
}
+ public OpenClosedInterfacesOptions suppressSingleOpenInterface(ClassReference classReference) {
+ assert !allowOpenInterfaces;
+ suppressions.add(
+ (appView, valueType, openInterface) ->
+ openInterface.getTypeName().equals(classReference.getTypeName()));
+ return this;
+ }
+
public void suppressAllOpenInterfaces() {
assert !allowOpenInterfaces;
suppressions.add((appView, valueType, openInterface) -> true);
@@ -1931,8 +1939,6 @@
// minified field names instead of original field names.
public boolean enableRecordModeling = true;
- public boolean allowConflictingSyntheticTypes = false;
-
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
public boolean enableD8ResourcesPassThrough = false;
diff --git a/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java b/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java
index e3aac9b..1724acc 100644
--- a/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/UnverifiableCfCodeDiagnostic.java
@@ -37,12 +37,13 @@
@Override
public String getDiagnosticMessage() {
- return "Unverifiable code in `"
- + MethodReferenceUtils.toSourceString(methodReference)
- + "` at instruction "
- + instructionIndex
- + ": "
- + message
- + ".";
+ StringBuilder builder =
+ new StringBuilder("Unverifiable code in `")
+ .append(MethodReferenceUtils.toSourceString(methodReference))
+ .append("`");
+ if (instructionIndex >= 0) {
+ builder.append(" at instruction ").append(instructionIndex);
+ }
+ return builder.append(": ").append(message).append(".").toString();
}
}
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
index 5a9504a..3635115 100644
--- a/src/test/examples/shaking1/print-mapping-dex.ref
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -4,5 +4,5 @@
3:5:void <init>(java.lang.String):13:13 -> <init>
0:6:void main(java.lang.String[]):8:8 -> main
7:21:void main(java.lang.String[]):9:9 -> main
- 0:19:java.lang.String method():17:17 -> a
+ 0:16:java.lang.String method():17:17 -> a
java.lang.String name -> a
diff --git a/src/test/examplesJava17/nest/NestLambda.java b/src/test/examplesJava17/nest/NestLambda.java
new file mode 100644
index 0000000..2c690b5
--- /dev/null
+++ b/src/test/examplesJava17/nest/NestLambda.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 nest;
+
+import java.util.function.Consumer;
+
+public class NestLambda {
+
+ private void print(Object o) {
+ System.out.println("printed: " + o);
+ }
+
+ Inner getInner() {
+ return new Inner();
+ }
+
+ class Inner {
+
+ void exec(Consumer<Object> consumer) {
+ consumer.accept("inner");
+ }
+
+ void execLambda() {
+ exec(NestLambda.this::print);
+ }
+ }
+
+ public static void main(String[] args) {
+ new NestLambda().getInner().execLambda();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index a7759e3..7b5d60b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -131,7 +131,7 @@
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
.run();
}
@@ -170,7 +170,7 @@
.withMinApiLevel(AndroidApiLevel.N)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 4330b97..69f2167 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -15,7 +15,6 @@
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;
@@ -123,8 +122,8 @@
public static final String R8_TEST_BUCKET = "r8-test-results";
- public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.2.jar";
- public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.2.jar";
+ public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.3.jar";
+ public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.3.jar";
public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar");
@@ -180,8 +179,6 @@
public static final Path DEPS = Paths.get(LIBS_DIR, "deps_all.jar");
public static final Path R8_RETRACE_JAR = Paths.get(LIBS_DIR, "r8retrace.jar");
- public static final Path DESUGAR_LIB_CONVERSIONS =
- Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
public static final String DESUGARED_LIB_RELEASES_DIR =
OPEN_JDK_DIR + "desugar_jdk_libs_releases/";
public static final Path DESUGARED_JDK_8_LIB_JAR =
@@ -189,8 +186,10 @@
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 getDesugarLibConversions(CustomConversionVersion legacy) {
+ return legacy == CustomConversionVersion.LEGACY
+ ? Paths.get(LIBS_DIR, "library_desugar_conversions_legacy.jar")
+ : Paths.get(LIBS_DIR, "library_desugar_conversions.jar");
}
public static Path getUndesugaredJdk11LibJarForTesting() {
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 f7ce233..b26a1cf 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
@@ -100,11 +100,6 @@
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.compile()
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
index c3c79fb..49d84b1 100644
--- a/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/StackMapVerificationNoFrameForHandlerTest.java
@@ -95,7 +95,11 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.allowDiagnosticWarningMessages(!includeFrameInHandler)
- .addOptionsModification(options -> options.testing.readInputStackMaps = true)
+ .addOptionsModification(
+ options -> {
+ options.getCfCodeAnalysisOptions().setEnableUnverifiableCodeReporting(false);
+ options.testing.readInputStackMaps = true;
+ })
.compileWithExpectedDiagnostics(this::verifyWarningsRegardingStackMap)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
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 efee088..5a05e80 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
@@ -54,7 +54,7 @@
"JDK8_CL",
ImmutableSet.of(
DESUGARED_JDK_8_LIB_JAR,
- ToolHelper.getConvertedDesugaredLibConversions(LEGACY),
+ ToolHelper.getDesugarLibConversions(LEGACY),
ToolHelper.getCoreLambdaStubs()),
JDK8.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.O)),
@@ -65,7 +65,7 @@
"JDK11_CL",
ImmutableSet.of(
ToolHelper.getUndesugaredJdk11LibJarForTesting(),
- ToolHelper.getConvertedDesugaredLibConversions(LATEST),
+ ToolHelper.getDesugarLibConversions(LATEST),
ToolHelper.getCoreLambdaStubs()),
JDK11.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.R)),
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
deleted file mode 100644
index 746a1d2..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/ConversionConverter.java
+++ /dev/null
@@ -1,179 +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.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/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 143a52d..b3cc9ab 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
@@ -168,7 +168,7 @@
CustomConversionVersion legacy) {
this(
name,
- ImmutableSet.of(desugarJdkLibs, ToolHelper.getConvertedDesugaredLibConversions(legacy)),
+ ImmutableSet.of(desugarJdkLibs, ToolHelper.getDesugarLibConversions(legacy)),
Paths.get("src/library_desugar/" + specificationPath),
ImmutableSet.of(ToolHelper.getAndroidJar(androidJarLevel)),
descriptor,
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
index 1a6e364..e862e2e 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
@@ -58,7 +58,6 @@
.addProgramClassFileData(getConflictingNameClass())
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@@ -70,7 +69,6 @@
.addProgramClassFileData(getConflictingNameClass())
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
.addKeepMainRule(TestClass.class)
// Ensure that R8 cannot remove or rename the conflicting name.
.addKeepClassAndMembersRules(CONFLICTING_NAME.getTypeName())
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestLambdaJava17Test.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestLambdaJava17Test.java
new file mode 100644
index 0000000..94fbe71
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestLambdaJava17Test.java
@@ -0,0 +1,81 @@
+// 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.nestaccesscontrol;
+
+import static com.android.tools.r8.utils.AndroidApiLevel.B;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NestLambdaJava17Test extends TestBase {
+
+ public NestLambdaJava17Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private static final Path JDK17_JAR =
+ Paths.get(ToolHelper.TESTS_BUILD_DIR, "examplesJava17").resolve("nest" + JAR_EXTENSION);
+ private static final String MAIN = "nest.NestLambda";
+ private static final String EXPECTED_RESULT = "printed: inner";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+ // The test requires the java.util.function. package.
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withApiLevel(AndroidApiLevel.N)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ Assume.assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramFiles(JDK17_JAR)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testJavaD8() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramFiles(JDK17_JAR)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().equals(B));
+ testForR8(parameters.getBackend())
+ .addProgramFiles(JDK17_JAR)
+ .applyIf(
+ parameters.isCfRuntime(),
+ // Alternatively we need to pass Jdk17 as library.
+ b -> b.addKeepRules("-dontwarn java.lang.invoke.StringConcatFactory"))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+}
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 ddd9bd4..d7878b9 100644
--- a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
+++ b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
@@ -49,6 +49,8 @@
.addDontWarn("dalvik.system.VMStack")
.addDontWarn("zzz.com.facebook.litho.R$id")
.addDontWarn("com.google.android.libraries.elements.R$id")
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.allowUnnecessaryDontWarnWildcards()
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index 128f780..eef3dcf 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -93,6 +93,8 @@
.addClasspathFiles(outDirectory.resolve("classpath.jar"))
.addLibraryFiles(outDirectory.resolve("library.jar"))
.addKeepRuleFiles(outDirectory.resolve("proguard.config"))
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.setMinApi(AndroidApiLevel.M)
.allowDiagnosticMessages()
.allowUnnecessaryDontWarnWildcards()
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
index e654b89..ce06fd4 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.ir.analysis.type;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticException;
+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.CoreMatchers.equalTo;
import static org.junit.Assert.assertFalse;
@@ -16,8 +19,11 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.google.common.base.Throwables;
import java.util.List;
import org.junit.Test;
@@ -75,16 +81,27 @@
.setMinApi(parameters.getApiLevel())
.compileWithExpectedDiagnostics(
diagnostics -> {
- if (!allowTypeErrors) {
+ if (allowTypeErrors) {
+ MethodReference mainMethodReference =
+ MethodReferenceUtils.mainMethod(TestClass.class);
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `"
+ + MethodReferenceUtils.toSourceString(mainMethodReference)
+ + "`"))),
+ diagnosticMessage(
+ equalTo(
+ "The method `"
+ + MethodReferenceUtils.toSourceString(mainMethodReference)
+ + "` does not type check and will be assumed to "
+ + "be unreachable.")));
+ } else {
diagnostics.assertErrorThatMatches(diagnosticException(AssertionError.class));
}
- })
- .assertAllWarningMessagesMatch(
- equalTo(
- "The method `void "
- + TestClass.class.getTypeName()
- + ".main(java.lang.String[])` does not type check and will be assumed to"
- + " be unreachable."));
+ });
// Compilation fails unless type errors are allowed.
assertTrue(allowTypeErrors);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 4f8c88c..3bf42fa 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -449,10 +449,8 @@
@Test
public void joinPrimitiveAndClassArrays() {
assertEquals(
- array(3, factory.objectType),
- join(
- array(4, factory.intType),
- array(3, factory.stringType)));
+ array(array(array(element(factory.objectType, maybeNull(), factory.serializableType)))),
+ join(array(4, factory.intType), array(3, factory.stringType)));
}
@Test
@@ -476,10 +474,8 @@
@Test
public void joinDistinctNestingClassArrays() {
assertEquals(
- array(3, factory.objectType),
- join(
- array(3, factory.stringType),
- array(4, factory.stringType)));
+ array(array(array(element(factory.objectType, maybeNull(), factory.serializableType)))),
+ join(array(3, factory.stringType), array(4, factory.stringType)));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StringCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StringCanonicalizationTest.java
index a8b1557..3aa8509 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StringCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StringCanonicalizationTest.java
@@ -6,7 +6,6 @@
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.Assume.assumeTrue;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.NeverInline;
@@ -159,7 +158,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
// CF should not canonicalize strings or lower them. See (r8g/30163) and (r8g/30320).
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
private final TestParameters parameters;
@@ -202,24 +201,25 @@
}
@Test
- public void testD8() throws Exception {
- assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
+ public void testR8Debug() throws Exception {
+ D8TestCompileResult result =
+ testForD8()
+ .debug()
+ .addProgramClassesAndInnerClasses(MAIN)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ test(result, 2, 1, 1, 1, 1);
+ }
+ @Test
+ public void testD8Release() throws Exception {
D8TestCompileResult result =
testForD8()
.release()
.addProgramClassesAndInnerClasses(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
- test(result, 1, 1, 1, 1, 1);
-
- result =
- testForD8()
- .debug()
- .addProgramClassesAndInnerClasses(MAIN)
- .setMinApi(parameters.getRuntime())
- .compile();
- test(result, 2, 1, 1, 1, 1);
+ test(result, 1, 1, 1, 1, 0);
}
@Test
@@ -230,9 +230,8 @@
.enableProguardTestOptions()
.enableInliningAnnotations()
.addKeepMainRule(MessageLoader.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
- test(result, 1, 1, 1, 1, 1);
+ test(result, 1, 1, 1, 1, 0);
}
-
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
index 1107d60..1e43777 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
@@ -53,15 +55,15 @@
.addKeepMainRule(Main.class)
// Keep get() to prevent that we optimize it into having static return type A.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
- .addNoVerticalClassMergingAnnotations()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
.applyIf(
- !enableVerticalClassMerging,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
- .enableNoVerticalClassMergingAnnotations())
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
index d294a77..aeca839 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
@@ -53,15 +55,15 @@
.addKeepMainRule(Main.class)
// Keep get() to prevent that we optimize it into having static return type A.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
- .addNoVerticalClassMergingAnnotations()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
.applyIf(
- !enableVerticalClassMerging,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
- .enableNoVerticalClassMergingAnnotations())
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
index 87d60b4..f541549 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
@@ -8,8 +8,11 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.classmerger.vertical.StaticPutToInterfaceWithObjectMergingTest.I;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
@@ -53,15 +56,15 @@
.addKeepMainRule(Main.class)
// Keep get() to prevent that we optimize it into having static return type A.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
- .addNoVerticalClassMergingAnnotations()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
.applyIf(
- !enableVerticalClassMerging,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
- .enableNoVerticalClassMergingAnnotations())
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
index 072b819..bfc7276 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
@@ -8,8 +8,10 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
@@ -53,15 +55,15 @@
.addKeepMainRule(Main.class)
// Keep get() to prevent that we optimize it into having static return type A.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
- .addNoVerticalClassMergingAnnotations()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
.applyIf(
- !enableVerticalClassMerging,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
- .enableNoVerticalClassMergingAnnotations())
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
index d74eb4b..3aaa789 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -62,11 +63,19 @@
.addKeepMainRule(Main.class)
// Keep get() to prevent that we optimize it into having static return type A.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
- .addInliningAnnotations()
- .addNoVerticalClassMergingAnnotations()
- .applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class)))
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ enableInlining,
+ R8TestBuilder::addInliningAnnotations,
+ R8TestBuilder::enableInliningAnnotations)
+ .applyIf(
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
index 4dc19d3..7bc991e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
@@ -62,16 +63,20 @@
// Keep getA() and getB() to prevent that we optimize it into having static return type A/B.
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get?(...); }")
.addInliningAnnotations()
- .addNoVerticalClassMergingAnnotations()
- .applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressSingleOpenInterface(Reference.classFromClass(I.class))
+ .suppressSingleOpenInterface(Reference.classFromClass(J.class)))
.applyIf(
- !enableVerticalClassMerging,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options ->
- options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
- .enableNoVerticalClassMergingAnnotations())
+ enableInlining,
+ R8TestBuilder::addInliningAnnotations,
+ R8TestBuilder::enableInliningAnnotations)
+ .applyIf(
+ enableVerticalClassMerging,
+ R8TestBuilder::addNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations)
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
index e8d4e5f..c756f8c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
@@ -41,7 +41,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -52,7 +52,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -68,7 +68,7 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -102,11 +102,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the check-cast instruction since I is open.
- return ImmutableList.of("OK", "OK");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime()
&& parameters
.getDexRuntimeVersion()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
index e2b4a29..4eaa6a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
@@ -71,8 +71,7 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- // TODO(b/214496607): Should succeed with the expected output.
- .assertFailureWithErrorThatThrows(ClassCastException.class);
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
index 5a57dc4..dac3848 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
@@ -41,7 +41,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -52,7 +52,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -68,7 +68,7 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -95,11 +95,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
- return ImmutableList.of("true", "true");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)) {
return ImmutableList.of("true", "true");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
index 68c055d..07b765f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -40,7 +40,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -51,7 +51,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -65,7 +65,7 @@
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -92,11 +92,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
- return ImmutableList.of("true");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime()) {
if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)
|| parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
index 7ba5906..7873c68 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
@@ -81,6 +81,7 @@
// To trigger outliner, set # of expected outline candidate as threshold.
options.outline.threshold = 2;
options.inlinerOptions().enableInlining = false;
+ options.enableStringConcatenationOptimization = false;
});
ProcessResult result = runOnArtRaw(app, TestClass.class);
assertEquals(result.toString(), 0, result.exitCode);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java
deleted file mode 100644
index cc582ea..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisSmaliTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (c) 2019, 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.StringBuilderOptimizerAnalysisTest.checkBuilderState;
-import static com.android.tools.r8.ir.optimize.string.StringBuilderOptimizerAnalysisTest.checkOptimizerStates;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.analysis.AnalysisTestBase;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer.BuilderState;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.smali.SmaliBuilder;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.util.Map;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class StringBuilderOptimizerAnalysisSmaliTest extends AnalysisTestBase {
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- public StringBuilderOptimizerAnalysisSmaliTest(TestParameters parameters) throws Exception {
- super(parameters, buildApp(), "TestClass");
- }
-
- private static AndroidApp buildApp() throws Exception {
- SmaliBuilder smaliBuilder = new SmaliBuilder("TestClass");
-
- {
- // IR of StringConcatenationTestClass#phiAtInit at DVM older than or equal to 5.1.1
- String code =
- StringUtils.lines(
- "invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
- "move-result-wide v0",
- "const-wide/16 v2, 0",
- "cmp-long v4, v0, v2",
- // new-instance is hoisted, but <init> calls are still at each branch
- "new-instance v0, Ljava/lang/StringBuilder;",
- "if-lez v4, :cond",
- "const-string v1, \"Hello\"",
- "invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
- "goto :merge",
- ":cond",
- "const-string v1, \"Hi\"",
- "invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
- "goto :merge",
- ":merge",
- "const-string v1, \", R8\"",
- "invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
- + "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- "sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- "invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
- "move-result-object v1",
- "invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
- "return-void");
- smaliBuilder.addStaticMethod(
- "void", "phiAtInit_5_1_1", ImmutableList.of(), 5, code);
- }
-
- {
- // Compiled StringConcatenationTestClass#phiAtInit and modified two new-instance instructions
- // are flown to the same <init> call.
- String code =
- StringUtils.lines(
- "invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
- "move-result-wide v0",
- "const-wide/16 v2, 0",
- "cmp-long v4, v0, v2",
- "if-lez v4, :cond",
- "new-instance v0, Ljava/lang/StringBuilder;",
- "const-string v1, \"Hello\"",
- "goto :merge",
- ":cond",
- "new-instance v0, Ljava/lang/StringBuilder;",
- "const-string v1, \"Hi\"",
- "goto :merge",
- ":merge",
- // Two separate new-instance instructions are flown to the same <init>.
- "invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
- "const-string v1, \", R8\"",
- "invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
- + "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- "sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- "invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
- "move-result-object v1",
- "invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
- "return-void");
- smaliBuilder.addStaticMethod(
- "void", "phiWithDifferentNewInstance", ImmutableList.of(), 5, code);
- }
-
- {
- // IR of StringConcatenationTestClass#phiAtInit at DVM/ART newer than 5.1.1
- String code =
- StringUtils.lines(
- "invoke-static {}, Ljava/lang/System;->currentTimeMillis()J",
- "move-result-wide v0",
- "const-wide/16 v2, 0",
- "cmp-long v4, v0, v2",
- // new-instance and <init> are in each branch.
- "if-lez v4, :cond",
- "new-instance v0, Ljava/lang/StringBuilder;",
- "const-string v1, \"Hello\"",
- "invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
- "goto :merge",
- ":cond",
- "new-instance v0, Ljava/lang/StringBuilder;",
- "const-string v1, \"Hi\"",
- "invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V",
- "goto :merge",
- ":merge",
- "const-string v1, \", R8\"",
- "invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append"
- + "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- "sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;",
- "invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
- "move-result-object v1",
- "invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
- "return-void");
- smaliBuilder.addStaticMethod(
- "void", "phiAtInit", ImmutableList.of(), 5, code);
- }
-
- return AndroidApp.builder()
- .addDexProgramData(smaliBuilder.compile(), Origin.unknown())
- .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
- .build();
- }
-
- @Test
- public void testPhiAtInit_5_1_1() {
- buildAndCheckIR(
- "phiAtInit_5_1_1",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, true);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testPhiWithDifferentNewInstance() {
- buildAndCheckIR(
- "phiWithDifferentNewInstance",
- checkOptimizerStates(
- appView,
- optimizer -> {
- assertEquals(0, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testPhiAtInit() {
- buildAndCheckIR(
- "phiAtInit",
- checkOptimizerStates(
- appView,
- optimizer -> {
- assertEquals(0, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
deleted file mode 100644
index 2a81ef0..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizerAnalysisTest.java
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright (c) 2019, 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 org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.ir.analysis.AnalysisTestBase;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer.BuilderState;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class StringBuilderOptimizerAnalysisTest extends AnalysisTestBase {
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
- }
-
- public StringBuilderOptimizerAnalysisTest(TestParameters parameters) throws Exception {
- super(
- parameters,
- StringConcatenationTestClass.class.getTypeName(),
- StringConcatenationTestClass.class);
- }
-
- @Override
- public void configure(InternalOptions options) {}
-
- @Test
- public void testUnusedBuilder() {
- buildAndCheckIR(
- "unusedBuilder",
- code -> assertTrue(code.streamInstructions().allMatch(Instruction::isReturn)));
- }
-
- @Test
- public void testTrivialSequence() {
- buildAndCheckIR(
- "trivialSequence",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "xyz", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testBuilderWithInitialValue() {
- buildAndCheckIR(
- "builderWithInitialValue",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "Hello,R8", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testBuilderWithCapacity() {
- buildAndCheckIR(
- "builderWithCapacity",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "42", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testNonStringArgs() {
- buildAndCheckIR(
- "nonStringArgs",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "42", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testTypeConversion() {
- buildAndCheckIR(
- "typeConversion",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "0.14 0 false null", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testTypeConversion_withPhis() {
- buildAndCheckIR(
- "typeConversion_withPhis",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, true);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): passed to another builder should be an eligible case.")
- @Test
- public void testNestedBuilders_appendBuilderItself() {
- buildAndCheckIR(
- "nestedBuilders_appendBuilderItself",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "Hello,R8", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(2, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): merge builder.")
- @Test
- public void testNestedBuilders_appendBuilderResult() {
- buildAndCheckIR(
- "nestedBuilders_appendBuilderResult",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "Hello,R8", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(2, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): merge builder.")
- @Test
- public void testNestedBuilders_conditional() {
- buildAndCheckIR(
- "nestedBuilders_conditional",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, true);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(3, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): merge builder.")
- @Test
- public void testConcatenatedBuilders_init() {
- buildAndCheckIR(
- "concatenatedBuilders_init",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "Hello,R8", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(2, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): merge builder.")
- @Test
- public void testConcatenatedBuilders_append() {
- buildAndCheckIR(
- "concatenatedBuilders_append",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "Hello,R8", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(2, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Ignore("TODO(b/113859361): merge builder.")
- @Test
- public void testConcatenatedBuilders_conditional() {
- final Set<String> expectedConstStrings = new HashSet<>();
- expectedConstStrings.add("Hello,R8");
- expectedConstStrings.add("D8");
- buildAndCheckIR(
- "concatenatedBuilders_conditional",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(2, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderStates(optimizer, perBuilderState, expectedConstStrings, true);
- }
- assertEquals(2, optimizer.analysis.simplifiedBuilders.size());
- // TODO(b/113859361): check # of merged builders.
- // assertEquals(2, optimizer.analysis.mergedBuilders.size());
- }));
- }
-
- @Test
- public void testSimplePhi() {
- buildAndCheckIR(
- "simplePhi",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(0, optimizer.analysis.builderStates.size());
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testPhiAtInit() {
- int expectedNumOfNewBuilder = 0;
- boolean expectToMeetToString = false;
- if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.M)) {
- expectedNumOfNewBuilder = 1;
- expectToMeetToString = true;
- }
- final int finalExpectedNumOfNewBuilder = expectedNumOfNewBuilder;
- final boolean finalExpectToMeetToString = expectToMeetToString;
- buildAndCheckIR(
- "phiAtInit",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(finalExpectedNumOfNewBuilder, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, finalExpectToMeetToString);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testPhiWithDifferentInits() {
- buildAndCheckIR(
- "phiWithDifferentInits",
- checkOptimizerStates(
- appView,
- optimizer -> {
- assertEquals(0, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, false);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testConditionalPhiWithoutAppend() {
- buildAndCheckIR(
- "conditionalPhiWithoutAppend",
- checkOptimizerStates(
- appView,
- optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, "initial:suffix", true);
- }
- assertEquals(1, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testLoop() {
- buildAndCheckIR(
- "loop",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(2, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, true);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- @Test
- public void testLoopWithBuilder() {
- buildAndCheckIR(
- "loopWithBuilder",
- checkOptimizerStates(appView, optimizer -> {
- assertEquals(1, optimizer.analysis.builderStates.size());
- for (Value builder : optimizer.analysis.builderStates.keySet()) {
- Map<Instruction, BuilderState> perBuilderState =
- optimizer.analysis.builderStates.get(builder);
- checkBuilderState(optimizer, perBuilderState, null, true);
- }
- assertEquals(0, optimizer.analysis.simplifiedBuilders.size());
- }));
- }
-
- static Consumer<IRCode> checkOptimizerStates(
- AppView<?> appView, Consumer<StringBuilderOptimizer> optimizerConsumer) {
- return code -> {
- StringBuilderOptimizer optimizer = new StringBuilderOptimizer(appView);
- optimizer.computeTrivialStringConcatenation(code);
- optimizerConsumer.accept(optimizer);
- };
- }
-
- static void checkBuilderState(
- StringBuilderOptimizer optimizer,
- Map<Instruction, BuilderState> perBuilderState,
- String expectedConstString,
- boolean expectToSeeMethodToString) {
- boolean seenMethodToString = false;
- for (Map.Entry<Instruction, BuilderState> entry : perBuilderState.entrySet()) {
- InvokeMethod invoke = entry.getKey().asInvokeMethod();
- if (invoke != null
- && optimizer.optimizationConfiguration.isToStringMethod(invoke.getInvokedMethod())) {
- seenMethodToString = true;
- Value builder = invoke.getArgument(0).getAliasedValue();
- assertEquals(
- expectedConstString, optimizer.analysis.toCompileTimeString(builder, entry.getValue()));
- }
- }
- assertEquals(expectToSeeMethodToString, seenMethodToString);
- }
-
- static void checkBuilderStates(
- StringBuilderOptimizer optimizer,
- Map<Instruction, BuilderState> perBuilderState,
- Set<String> expectedConstStrings,
- boolean expectToSeeMethodToString) {
- boolean seenMethodToString = false;
- for (Map.Entry<Instruction, BuilderState> entry : perBuilderState.entrySet()) {
- InvokeMethod invoke = entry.getKey().asInvokeMethod();
- if (invoke != null
- && optimizer.optimizationConfiguration.isToStringMethod(invoke.getInvokedMethod())) {
- seenMethodToString = true;
- Value builder = invoke.getArgument(0).getAliasedValue();
- String computedString = optimizer.analysis.toCompileTimeString(builder, entry.getValue());
- assertNotNull(computedString);
- assertTrue(expectedConstStrings.contains(computedString));
- expectedConstStrings.remove(computedString);
- }
- }
- assertEquals(expectToSeeMethodToString, seenMethodToString);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
index f908b95..365ced3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
@@ -37,7 +37,7 @@
@Parameters(name = "{0}, configuration: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), getTestExpectations());
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(), getTestExpectations());
}
private static class StringBuilderResult {
@@ -94,14 +94,14 @@
StringBuilderResult.create(
Main.class.getMethod("materializingWithAdditionalAppend"),
StringUtils.lines("Hello World", "Hello WorldObservable"),
- 1,
- 3,
- 2),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
Main.class.getMethod("appendWithNonConstant"),
StringUtils.lines("Hello World, Hello World"),
1,
- 3,
+ 2,
1),
StringBuilderResult.create(
Main.class.getMethod("simpleLoopTest"),
@@ -109,13 +109,12 @@
1,
1,
1),
- // TODO(b/222437581): Should not remove StringBuilder
StringBuilderResult.create(
Main.class.getMethod("simpleLoopTest2"),
StringUtils.lines("Hello World", "Hello WorldHello World"),
- 0,
- 0,
- 0),
+ 1,
+ 1,
+ 1),
StringBuilderResult.create(
Main.class.getMethod("simpleLoopWithStringBuilderInBodyTest"),
StringUtils.lines("Hello World"),
@@ -129,27 +128,27 @@
0,
0),
StringBuilderResult.create(
- Main.class.getMethod("diamondWithUseTest"), StringUtils.lines("Hello World"), 1, 3, 1),
+ Main.class.getMethod("diamondWithUseTest"), StringUtils.lines("Hello World"), 1, 2, 1),
StringBuilderResult.create(
Main.class.getMethod("diamondsWithSingleUseTest"),
StringUtils.lines("Hello World"),
1,
- 3,
+ 2,
1),
StringBuilderResult.create(
Main.class.getMethod("escapeTest"), StringUtils.lines("Hello World"), 2, 2, 1),
StringBuilderResult.create(
Main.class.getMethod("intoPhiTest"), StringUtils.lines("Hello World"), 2, 2, 1),
StringBuilderResult.create(
- Main.class.getMethod("optimizePartial"), StringUtils.lines("Hello World.."), 1, 4, 1),
+ Main.class.getMethod("optimizePartial"), StringUtils.lines("Hello World.."), 1, 2, 1),
StringBuilderResult.create(
Main.class.getMethod("multipleToStrings"),
StringUtils.lines("Hello World", "Hello World.."),
- 1,
- 4,
- 2),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
- Main.class.getMethod("changeAppendType"), StringUtils.lines("1 World"), 1, 3, 1),
+ Main.class.getMethod("changeAppendType"), StringUtils.lines("1 World"), 1, 2, 1),
StringBuilderResult.create(
Main.class.getMethod("checkCapacity"), StringUtils.lines("true"), 2, 1, 0),
StringBuilderResult.create(
@@ -157,29 +156,29 @@
StringBuilderResult.create(
Main.class.getMethod("stringBuilderWithStringBuilderToString"),
StringUtils.lines("Hello World"),
- 1,
- 1,
- 1),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
Main.class.getMethod("stringBuilderWithStringBuilder"),
StringUtils.lines("Hello World"),
- 2,
- 2,
- 1),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
Main.class.getMethod("stringBuilderInStringBuilderConstructor"),
StringUtils.lines("Hello World"),
- 2,
- 1,
- 1),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
Main.class.getMethod("interDependencyTest"),
StringUtils.lines("World Hello World "),
- 2,
- 2,
- 1),
+ 0,
+ 0,
+ 0),
StringBuilderResult.create(
- Main.class.getMethod("stringBuilderSelfReference"), StringUtils.lines(""), 1, 1, 1),
+ Main.class.getMethod("stringBuilderSelfReference"), StringUtils.lines(""), 0, 0, 0),
StringBuilderResult.create(
Main.class.getMethod("unknownStringBuilderInstruction"),
StringUtils.lines("Hello World"),
@@ -214,8 +213,6 @@
@Test
public void testR8() throws Exception {
- boolean hasError =
- stringBuilderTest.getMethodName().equals("simpleLoopTest2") && parameters.isDexRuntime();
compilationResults
.apply(parameters)
.inspect(
@@ -235,9 +232,7 @@
stringBuilderTest.toStrings, countStringBuilderToStrings(foundMethodSubject));
})
.run(parameters.getRuntime(), Main.class, stringBuilderTest.getMethodName())
- // TODO(b/222437581): Incorrect result for string builder inside loop.
- .assertSuccessWithOutputLinesIf(hasError, "Hello World", "Hello World")
- .assertSuccessWithOutputIf(!hasError, stringBuilderTest.expected);
+ .assertSuccessWithOutput(stringBuilderTest.expected);
}
private long countStringBuilderInits(FoundMethodSubject method) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectMutationThroughPhiTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectMutationThroughPhiTest.java
index bd6fffd..88f0cf8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectMutationThroughPhiTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectMutationThroughPhiTest.java
@@ -41,10 +41,8 @@
.inspect(
inspector -> {
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
- // TODO(b/114002137): This should be optimized into having only 3 append calls,
- // since append("baz").append("qux") can be optimized into append("bazqux").
assertEquals(
- 4,
+ parameters.isCfRuntime() ? 4 : 3,
mainMethodSubject
.streamInstructions()
.filter(isInvokeStringBuilderAppendWithString())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectPhiMutationThroughPhiOperandTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectPhiMutationThroughPhiOperandTest.java
index b553e2d..81fa83b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectPhiMutationThroughPhiOperandTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithIndirectPhiMutationThroughPhiOperandTest.java
@@ -41,10 +41,9 @@
.inspect(
inspector -> {
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
- // TODO(b/114002137): This should be optimized into having only 3 append calls,
- // since append("baz").append("qux") can be optimized into append("bazqux").
+ // TODO(b/114002137): Also run for CF
assertEquals(
- 4,
+ parameters.isCfRuntime() ? 4 : 3,
mainMethodSubject
.streamInstructions()
.filter(isInvokeStringBuilderAppendWithString())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
index ef8217a..617fd63 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
@@ -9,7 +9,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
@@ -113,27 +112,27 @@
method = mainClass.uniqueMethodWithName("trivialSequence");
assertThat(method, isPresent());
- expectedCount = isR8 ? 1 : 3;
+ expectedCount = isReleaseMode ? 1 : 3;
assertEquals(expectedCount, countConstString(method));
method = mainClass.uniqueMethodWithName("builderWithInitialValue");
assertThat(method, isPresent());
- expectedCount = isR8 ? 1 : 3;
+ expectedCount = isReleaseMode ? 1 : 3;
assertEquals(expectedCount, countConstString(method));
method = mainClass.uniqueMethodWithName("builderWithCapacity");
assertThat(method, isPresent());
- expectedCount = isR8 ? 1 : 0;
+ expectedCount = isReleaseMode ? 1 : 0;
assertEquals(expectedCount, countConstString(method));
method = mainClass.uniqueMethodWithName("nonStringArgs");
assertThat(method, isPresent());
- expectedCount = isR8 ? 1 : 0;
+ expectedCount = isReleaseMode ? 1 : 0;
assertEquals(expectedCount, countConstString(method));
method = mainClass.uniqueMethodWithName("typeConversion");
assertThat(method, isPresent());
- expectedCount = isR8 ? 1 : 0;
+ expectedCount = isReleaseMode ? 1 : 0;
assertEquals(expectedCount, countConstString(method));
method = mainClass.uniqueMethodWithName("typeConversion_withPhis");
@@ -142,41 +141,31 @@
method = mainClass.uniqueMethodWithName("nestedBuilders_appendBuilderItself");
assertThat(method, isPresent());
- // TODO(b/113859361): merge builders
- expectedCount = 3;
- assertEquals(expectedCount, countConstString(method));
+ assertEquals(isReleaseMode ? 1 : 3, countConstString(method));
method = mainClass.uniqueMethodWithName("nestedBuilders_appendBuilderResult");
assertThat(method, isPresent());
- // TODO(b/113859361): merge builders
- expectedCount = 3;
- assertEquals(expectedCount, countConstString(method));
+ assertEquals(isReleaseMode ? 1 : 3, countConstString(method));
method = mainClass.uniqueMethodWithName("nestedBuilders_conditional");
assertThat(method, isPresent());
- assertEquals(4, countConstString(method));
+ assertEquals(isReleaseMode ? 3 : 4, countConstString(method));
method = mainClass.uniqueMethodWithName("concatenatedBuilders_init");
assertThat(method, isPresent());
- // TODO(b/113859361): merge builders
- expectedCount = 2;
- assertEquals(expectedCount, countConstString(method));
+ assertEquals(isReleaseMode ? 1 : 2, countConstString(method));
method = mainClass.uniqueMethodWithName("concatenatedBuilders_append");
assertThat(method, isPresent());
- // TODO(b/113859361): merge builders
- expectedCount = 2;
- assertEquals(expectedCount, countConstString(method));
+ assertEquals(isReleaseMode ? 1 : 2, countConstString(method));
method = mainClass.uniqueMethodWithName("concatenatedBuilders_conditional");
assertThat(method, isPresent());
- // TODO(b/113859361): merge builders
- expectedCount = 4;
- assertEquals(expectedCount, countConstString(method));
+ assertEquals(isReleaseMode ? 2 : 4, countConstString(method));
method = mainClass.uniqueMethodWithName("simplePhi");
assertThat(method, isPresent());
- assertEquals(4, countConstString(method));
+ assertEquals(isReleaseMode ? 3 : 4, countConstString(method));
method = mainClass.uniqueMethodWithName("phiAtInit");
assertThat(method, isPresent());
@@ -188,7 +177,7 @@
method = mainClass.uniqueMethodWithName("conditionalPhiWithoutAppend");
assertThat(method, isPresent());
- assertEquals(3, countConstString(method));
+ assertEquals(isReleaseMode ? 2 : 3, countConstString(method));
method = mainClass.uniqueMethodWithName("loop");
assertThat(method, isPresent());
@@ -204,26 +193,31 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
-
- D8TestRunResult result =
+ test(
testForD8()
.debug()
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, false, false);
+ .assertSuccessWithOutput(JAVA_OUTPUT),
+ false,
+ false);
+ }
- result =
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue("Only run D8 for Dex backend", parameters.isDexRuntime());
+ test(
testForD8()
.release()
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, false, true);
+ .assertSuccessWithOutput(JAVA_OUTPUT),
+ false,
+ true);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
index 12bc414..8b958fd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
index 5bbd532..0fa90c1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -65,7 +65,7 @@
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
testForR8(parameters.getBackend())
.addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
- .addProgramFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
+ .addProgramFiles(kotlinc.getKotlinStdlibJar())
.addProgramFiles(kotlinc.getKotlinAnnotationJar())
.setMinApi(parameters.getApiLevel())
.allowAccessModification()
diff --git a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
index 170595a..37b20bd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.KotlinCompilerTool;
@@ -100,11 +101,18 @@
// TODO(b/190489514): We should be able to optimize constant stringPlus calls.
assertThat(methodSubject, CodeMatchers.invokesMethodWithName("stringPlus"));
}
- // TODO(b/219455761): StringBuilderOptimizer fails to remove constant input.
- assertThat(
- methodSubject,
- CodeMatchers.invokesMethodWithHolderAndName(
- typeName(StringBuilder.class), "append"));
+ // We cannot remove the <init> -> <append> call since that changes the capacity
+ // and the string builder is escaping into System.out.
+ assertEquals(
+ 1,
+ methodSubject
+ .streamInstructions()
+ .filter(
+ instructionSubject ->
+ CodeMatchers.isInvokeWithTarget(
+ typeName(StringBuilder.class), "append")
+ .test(instructionSubject))
+ .count());
})
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 2eb6a7f..25ffe36 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -10,77 +10,36 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.DiagnosticsMatcher;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClassAndMethod;
-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.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.code.CatchHandlers;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Phi.RegisterReadType;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.SyntheticPosition;
-import com.android.tools.r8.ir.code.ValueTypeConstraint;
-import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
-import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
-import com.android.tools.r8.utils.AbortException;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.transformers.ClassTransformer;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MainDexListParser;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableList;
@@ -89,26 +48,22 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.BeforeClass;
import org.junit.ClassRule;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
@RunWith(Parameterized.class)
public class MainDexListTests extends TestBase {
@@ -154,21 +109,22 @@
// Generates an application with many classes, every even in one package and every odd in
// another. Keep the number of methods low enough for single dex application.
- AndroidApp generated = generateApplication(
- MANY_CLASSES, AndroidApiLevel.getDefault().getLevel(),
- MANY_CLASSES_SINGLE_DEX_METHODS_PER_CLASS);
- generated.write(getManyClassesSingleDexAppPath(), OutputMode.DexIndexed);
+ generateApplication(
+ getManyClassesSingleDexAppPath(), MANY_CLASSES, MANY_CLASSES_SINGLE_DEX_METHODS_PER_CLASS);
// Generates an application with many classes, every even in one package and every odd in
// another. Add enough methods so the application cannot fit into one dex file.
- generated = generateApplication(
- MANY_CLASSES, AndroidApiLevel.L.getLevel(), MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
- generated.write(getManyClassesMultiDexAppPath(), OutputMode.DexIndexed);
+ generateApplication(
+ getManyClassesMultiDexAppPath(), MANY_CLASSES, MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
// Generates an application with two classes, each with the maximum possible number of methods.
- generated = generateApplication(TWO_LARGE_CLASSES, AndroidApiLevel.N.getLevel(),
- MAX_METHOD_COUNT);
- generated.write(getTwoLargeClassesAppPath(), OutputMode.DexIndexed);
+ generateApplication(getTwoLargeClassesAppPath(), TWO_LARGE_CLASSES, getLargeClassMethodCount());
+ }
+
+ private static int getLargeClassMethodCount() {
+ int otherConstantPoolEntries = 23;
+ int maxMethodCount = MAX_METHOD_COUNT - otherConstantPoolEntries;
+ return maxMethodCount;
}
private static Path getTwoLargeClassesAppPath() {
@@ -183,17 +139,10 @@
return generatedApplicationsFolder.getRoot().toPath().resolve("many-classes-stereo.zip");
}
- private static Path getManyClassesForceMultiDexAppPath() {
- return generatedApplicationsFolder.getRoot().toPath().resolve("many-classes-stereo-forced.zip");
- }
-
- private static Set<DexType> parse(Path path, DexItemFactory itemFactory) throws IOException {
+ private static Set<DexType> parse(Path path, DexItemFactory itemFactory) {
return MainDexListParser.parseList(StringResource.fromFile(path), itemFactory);
}
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
@Test
public void checkGeneratedFileFitInSingleDexFile() {
assertTrue(MANY_CLASSES_COUNT * MANY_CLASSES_SINGLE_DEX_METHODS_PER_CLASS <= MAX_METHOD_COUNT);
@@ -221,20 +170,23 @@
getTwoLargeClassesAppPath(),
false,
test -> {
- TestDiagnosticsHandler handler = new TestDiagnosticsHandler();
+ TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
try {
test.run(handler);
fail("Expect to fail, for there are too many classes for the main-dex list.");
} catch (Throwable e) {
assert e instanceof CompilationFailedException;
- assertEquals(1, handler.errors.size());
- DexFileOverflowDiagnostic overflow = (DexFileOverflowDiagnostic) handler.errors.get(0);
+ handler.assertErrorsMatch(
+ DiagnosticsMatcher.diagnosticType(DexFileOverflowDiagnostic.class));
+ DexFileOverflowDiagnostic overflow =
+ (DexFileOverflowDiagnostic) handler.getErrors().get(0);
// Make sure {@link MonoDexDistributor} was _not_ used, i.e., a spec was given.
assertTrue(overflow.hasMainDexSpecification());
// Make sure what exceeds the limit is the number of methods.
assertTrue(overflow.getNumberOfMethods() > overflow.getMaximumNumberOfMethods());
assertEquals(
- TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT, overflow.getNumberOfMethods());
+ TWO_LARGE_CLASSES.size() * getLargeClassMethodCount(),
+ overflow.getNumberOfMethods());
}
});
}
@@ -261,7 +213,7 @@
@Test
public void allClassesInMainDex() throws Throwable {
- // Degenerated case with an app thats fit into a single dex, and where the main dex list
+ // Degenerated case with an app that fits into a single dex, and where the main dex list
// contains all classes.
verifyMainDexContains(MANY_CLASSES, getManyClassesSingleDexAppPath(), true);
}
@@ -273,14 +225,16 @@
getManyClassesMultiDexAppPath(),
false,
test -> {
- TestDiagnosticsHandler handler = new TestDiagnosticsHandler();
+ TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
try {
test.run(handler);
fail("Expect to fail, for there are too many classes for the main-dex list.");
} catch (Throwable e) {
assert e instanceof CompilationFailedException;
- assertEquals(1, handler.errors.size());
- DexFileOverflowDiagnostic overflow = (DexFileOverflowDiagnostic) handler.errors.get(0);
+ handler.assertErrorsMatch(
+ DiagnosticsMatcher.diagnosticType(DexFileOverflowDiagnostic.class));
+ DexFileOverflowDiagnostic overflow =
+ (DexFileOverflowDiagnostic) handler.getErrors().get(0);
// Make sure {@link MonoDexDistributor} was _not_ used, i.e., a main-dex spec was given.
assertTrue(overflow.hasMainDexSpecification());
// Make sure what exceeds the limit is the number of methods.
@@ -552,44 +506,6 @@
.collect(Collectors.toList()), false);
}
- @Test
- public void checkIntermediateMultiDex() throws Exception {
- // Generates an application with many classes, every even in one package and every odd in
- // another. Add enough methods so the application cannot fit into one dex file.
- // Notice that this one allows multidex while using lower API.
- AndroidApp generated = generateApplication(
- MANY_CLASSES, AndroidApiLevel.K.getLevel(), true, MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
- generated.write(getManyClassesForceMultiDexAppPath(), OutputMode.DexIndexed);
- // Make sure the generated app indeed has multiple dex files.
- assertTrue(generated.getDexProgramResourcesForTesting().size() > 1);
- }
-
- @Test
- public void testMultiDexFailDueToMinApi() throws Exception {
- // Generates an application with many classes, every even in one package and every odd in
- // another. Add enough methods so the application cannot fit into one dex file.
- // Notice that this one fails due to the min API.
- TestDiagnosticsHandler handler = new TestDiagnosticsHandler();
- try {
- generateApplication(
- MANY_CLASSES,
- AndroidApiLevel.K.getLevel(),
- false,
- MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS,
- handler);
- fail("Expect to fail, for there are many classes while multidex is not enabled.");
- } catch (AbortException e) {
- assertEquals(1, handler.errors.size());
- DexFileOverflowDiagnostic overflow = (DexFileOverflowDiagnostic) handler.errors.get(0);
- // Make sure {@link MonoDexDistributor} was used, i.e., no main-dex specification was given.
- assertFalse(overflow.hasMainDexSpecification());
- // Make sure what exceeds the limit is the number of methods.
- assertEquals(
- MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS,
- overflow.getNumberOfMethods());
- }
- }
-
private static String typeToEntry(String type) {
return type.replace(".", "/") + FileUtils.CLASS_EXTENSION;
}
@@ -637,7 +553,7 @@
boolean minimalMainDex,
MultiDexTestMode testMode,
DiagnosticsHandler handler)
- throws IOException, ExecutionException, CompilationFailedException {
+ throws IOException, CompilationFailedException {
AndroidApp originalApp = AndroidApp.builder().addProgramFiles(app).build();
CodeInspector originalInspector = new CodeInspector(originalApp);
for (String clazz : mainDex) {
@@ -663,8 +579,8 @@
builder.addMainDexListFiles(mainDexList);
break;
case MULTIPLE_FILES: {
- // Partion the main dex list into several files.
- List<List<String>> partitions = Lists.partition(mainDex, Math.max(mainDex.size() / 3, 1));
+ // Partition the main dex list into several files.
+ List<List<String>> partitions = Lists.partition(mainDex, Math.max(mainDex.size() / 3, 1));
List<Path> mainDexListFiles = new ArrayList<>();
for (List<String> partition : partitions) {
Path partialMainDexList = temp.newFile().toPath();
@@ -725,7 +641,7 @@
singleDexApp,
test -> {
try {
- test.run(new TestDiagnosticsHandler());
+ test.run(new TestDiagnosticMessagesImpl());
} catch (Throwable e) {
throw new RuntimeException(e);
}
@@ -748,270 +664,46 @@
}
}
- public static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
- throws IOException, ExecutionException {
- return generateApplication(classes, minApi, false, methodCount);
+ private static void generateApplication(Path output, List<String> classes, int methodCount)
+ throws IOException {
+ ArchiveConsumer consumer = new ArchiveConsumer(output);
+ for (String typename : classes) {
+ String descriptor = DescriptorUtils.javaTypeToDescriptor(typename);
+ byte[] bytes =
+ transformer(ClassStub.class)
+ .setClassDescriptor(descriptor)
+ .addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access,
+ String name,
+ String descriptor,
+ String signature,
+ String[] exceptions) {
+ // This strips <init>() too.
+ if (name.equals("methodStub")) {
+ for (int i = 0; i < methodCount; i++) {
+ MethodVisitor mv =
+ super.visitMethod(
+ access, "method" + i, descriptor, signature, exceptions);
+ mv.visitCode();
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ }
+ return null;
+ }
+ })
+ .transform();
+ consumer.accept(ByteDataView.of(bytes), descriptor, null);
+ }
+ consumer.finished(null);
}
- private static AndroidApp generateApplication(
- List<String> classes, int minApi, boolean intermediate, int methodCount)
- throws IOException, ExecutionException {
- return generateApplication(
- classes, minApi, intermediate, methodCount, new DiagnosticsHandler() {});
- }
-
- private static AndroidApp generateApplication(
- List<String> classes,
- int minApi,
- boolean intermediate,
- int methodCount,
- DiagnosticsHandler diagnosticsHandler)
- throws IOException, ExecutionException {
- Timing timing = Timing.empty();
- InternalOptions options =
- new InternalOptions(new DexItemFactory(), new Reporter(diagnosticsHandler));
- options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApi));
- options.intermediate = intermediate;
- DexItemFactory factory = options.itemFactory;
- AppView<?> appView = AppView.createForR8(DexApplication.builder(options, timing).build());
- DexApplication.Builder<?> builder = DexApplication.builder(options, timing);
- for (String clazz : classes) {
- DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz));
- DexType type = factory.createType(desc);
- DexProgramClass programClass =
- new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("test", MainDexListTests.class),
- ClassAccessFlags.fromSharedAccessFlags(0),
- factory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- MethodCollectionFactory.empty(),
- false,
- DexProgramClass::invalidChecksumRequest);
- DexEncodedMethod[] directMethods = new DexEncodedMethod[methodCount];
- for (int i = 0; i < methodCount; i++) {
- MethodAccessFlags access = MethodAccessFlags.fromSharedAccessFlags(0, false);
- access.setPublic();
- access.setStatic();
- DexMethod voidReturnMethod =
- factory.createMethod(
- desc,
- factory.createString("method" + i),
- factory.voidDescriptor,
- DexString.EMPTY_ARRAY);
- Code code =
- new SynthesizedCode(
- (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition)) {
- @Override
- public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) {
- throw new Unreachable();
- }
- };
- DexEncodedMethod method =
- DexEncodedMethod.builder()
- .setMethod(voidReturnMethod)
- .setAccessFlags(access)
- .setCode(code)
- .disableAndroidApiLevelCheck()
- .build();
- ProgramMethod programMethod = new ProgramMethod(programClass, method);
- IRCode ir =
- code.buildIR(
- programMethod,
- appView,
- Origin.unknown(),
- new MutableMethodConversionOptions(options));
- RegisterAllocator allocator = new LinearScanRegisterAllocator(appView, ir);
- programMethod.setCode(
- new DexBuilder(ir, BytecodeMetadataProvider.empty(), allocator, options).build(),
- appView);
- directMethods[i] = method;
- }
- programClass.getMethodCollection().addDirectMethods(Arrays.asList(directMethods));
- builder.addProgramClass(programClass);
- }
- DirectMappedDexApplication application = builder.build().toDirect();
- ApplicationWriter writer =
- new ApplicationWriter(
- AppView.createForD8(
- AppInfo.createInitialAppInfo(
- application, GlobalSyntheticsStrategy.forNonSynthesizing())),
- null);
- ExecutorService executor = ThreadUtils.getExecutorService(options);
- AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
- try {
- writer.write(executor);
- } finally {
- executor.shutdown();
- }
- options.signalFinishedToConsumers();
- return compatSink.build();
- }
-
- // Code stub to generate methods with "return-void" bodies.
- private static class ReturnVoidCode implements SourceCode {
-
- private final Position position;
-
- public ReturnVoidCode(DexMethod method, Position callerPosition) {
- this.position =
- SyntheticPosition.builder()
- .setLine(0)
- .setMethod(method)
- .setCallerPosition(callerPosition)
- .build();
- }
-
- @Override
- public int instructionCount() {
- return 1;
- }
-
- @Override
- public int instructionIndex(int instructionOffset) {
- return instructionOffset;
- }
-
- @Override
- public int instructionOffset(int instructionIndex) {
- return instructionIndex;
- }
-
- @Override
- public DebugLocalInfo getIncomingLocalAtBlock(int register, int blockOffset) {
- return null;
- }
-
- @Override
- public DexType getPhiTypeForBlock(
- int register, int blockOffset, ValueTypeConstraint constraint, RegisterReadType readType) {
- throw new Unreachable("Should never generate a phi");
- }
-
- @Override
- public DebugLocalInfo getIncomingLocal(int register) {
- return null;
- }
-
- @Override
- public DebugLocalInfo getOutgoingLocal(int register) {
- return null;
- }
-
- @Override
- public int traceInstruction(int instructionIndex, IRBuilder builder) {
- return instructionIndex;
- }
-
- @Override
- public void setUp() {
- // Intentionally empty.
- }
-
- @Override
- public void clear() {
- // Intentionally empty.
- }
-
- @Override
- public void buildPrelude(IRBuilder builder) {
- // Intentionally empty.
- }
-
- @Override
- public void buildInstruction(
- IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
- assert instructionIndex == 0;
- builder.addReturn();
- }
-
- @Override
- public void buildBlockTransfer(
- IRBuilder builder, int predecessorOffset, int successorOffset, boolean isExceptional) {
- throw new Unreachable();
- }
-
- @Override
- public void buildPostlude(IRBuilder builder) {
- // Intentionally empty.
- }
-
- @Override
- public void resolveAndBuildSwitch(
- int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
- throw new Unreachable();
- }
-
- @Override
- public void resolveAndBuildNewArrayFilledData(
- int arrayRef, int payloadOffset, IRBuilder builder) {
- throw new Unreachable();
- }
-
- @Override
- public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
- return null;
- }
-
- @Override
- public int getMoveExceptionRegister(int instructionIndex) {
- throw new Unreachable();
- }
-
- @Override
- public Position getCanonicalDebugPositionAtOffset(int offset) {
- return Position.none();
- }
-
- @Override
- public Position getCurrentPosition() {
- return position;
- }
-
- @Override
- public boolean verifyRegister(int register) {
- throw new Unreachable();
- }
-
- @Override
- public boolean verifyCurrentInstructionCanThrow() {
- throw new Unreachable();
- }
-
- @Override
- public boolean verifyLocalInScope(DebugLocalInfo local) {
- throw new Unreachable();
- }
- }
-
- private class TestDiagnosticsHandler implements DiagnosticsHandler {
-
- public List<Diagnostic> errors = new ArrayList<>();
- public List<Diagnostic> warnings = new ArrayList<>();
-
- public int numberOfErrorsAndWarnings() {
- return errors.size() + warnings.size();
- }
-
- @Override
- public void error(Diagnostic error) {
- errors.add(error);
- }
-
- @Override
- public void warning(Diagnostic warning) {
- warnings.add(warning);
- }
+ // Simple stub/template for generating the input classes.
+ public static class ClassStub {
+ public static void methodStub() {}
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 013a15b..f375004 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.Reference;
@@ -60,16 +61,15 @@
private static final String EXAMPLE_SRC_DIR = ToolHelper.EXAMPLES_DIR;
private static final String EXAMPLE_O_SRC_DIR = ToolHelper.EXAMPLES_ANDROID_O_DIR;
- @Parameters(name = "{0}, {1}")
- public static List<Object[]> data() {
- return buildParameters(getTestParameters().withNoneRuntime().build(), Backend.values());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
}
- private final Backend backend;
+ private final Backend backend = Backend.CF;
- public MainDexTracingTest(TestParameters parameters, Backend backend) {
+ public MainDexTracingTest(TestParameters parameters) {
parameters.assertNoneRuntime();
- this.backend = backend;
}
private Path getInputJar(Path cfJar) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java
index 11bc862..85e2d31 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.missingclasses;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.diagnostic.DefinitionContext;
@@ -27,19 +28,25 @@
@Test(expected = CompilationFailedException.class)
public void testNoRules() throws Exception {
compileWithExpectedDiagnostics(
- Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ this::configure);
}
@Test
public void testDontWarnMainClass() throws Exception {
compileWithExpectedDiagnostics(
- Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::configure));
}
@Test
public void testDontWarnMissingClass() throws Exception {
compileWithExpectedDiagnostics(
- Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::configure));
}
@Test
@@ -47,7 +54,12 @@
compileWithExpectedDiagnostics(
Main.class,
diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
- addIgnoreWarnings());
+ addIgnoreWarnings().andThen(this::configure));
+ }
+
+ public void configure(R8FullTestBuilder testBuilder) {
+ testBuilder.addOptionsModification(
+ options -> options.getCfCodeAnalysisOptions().setEnableUnverifiableCodeReporting(false));
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
index 836c703..c7ca4d0 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/InterfaceInvokeWithNonTrivialButImpreciseStaticTypeTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
index f3b4ad9..81f261c 100644
--- a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
@@ -67,7 +67,7 @@
.addProgramClasses(A.class)
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(this::verifyVirtualCallToPrivate)
+ .inspect(this::verifyDirectCallToPrivate)
.writeToZip();
testForD8()
.addProgramFiles(desugaredJar)
@@ -77,7 +77,7 @@
.assertSuccessWithOutputLines("hep");
}
- private void verifyVirtualCallToPrivate(CodeInspector inspector) {
+ private void verifyDirectCallToPrivate(CodeInspector inspector) {
ClassSubject bClassSubject = inspector.clazz(PKG + ".B");
MethodSubject proceedMethodSubject = bClassSubject.uniqueMethodWithName("proceed");
assertThat(proceedMethodSubject, isPresent());
@@ -87,7 +87,7 @@
method ->
method
.streamInstructions()
- .filter(InstructionSubject::isInvokeVirtual)
+ .filter(InstructionSubject::isInvokeSpecialOrDirect)
.anyMatch(isInvokeWithTarget(proceedMethodSubject))));
}
}
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 077e3c2..7028257 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
@@ -4,7 +4,9 @@
package com.android.tools.r8.regress.b78493232;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.AsmTestBase;
@@ -96,12 +98,14 @@
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."))))
+ allOf(
+ startsWith(
+ "Unverifiable code in `java.lang.String regress78493232.Test."
+ + "methodCausingIssue(byte, short, int)`"),
+ containsString(
+ "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/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index 84f5522..e1e73b8 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -38,7 +38,12 @@
RetraceApiOutlineInlineTest.ApiTest.class,
RetraceApiOutlineInOutlineStackTrace.ApiTest.class,
RetraceApiInlineInOutlineTest.ApiTest.class,
- RetraceApiSingleFrameTest.ApiTest.class);
+ RetraceApiSingleFrameTest.ApiTest.class,
+ RetracePartitionStringTest.ApiTest.class,
+ RetracePartitionRoundTripTest.ApiTest.class,
+ RetracePartitionJoinNoMetadataTest.ApiTest.class,
+ RetracePartitionSerializedObfuscatedKeyTest.ApiTest.class,
+ RetracePartitionRoundTripInlineTest.ApiTest.class);
public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
ImmutableList.of();
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java
new file mode 100644
index 0000000..095eb99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionJoinNoMetadataTest.java
@@ -0,0 +1,99 @@
+// 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.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionJoinNoMetadataTest extends RetraceApiTestBase {
+
+ public RetracePartitionJoinNoMetadataTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final String aMapping =
+ "com.foo.bar.baz -> a:\n"
+ + " # {'id':'sourceFile','fileName':'BarBaz.kt'}\n"
+ + " int field -> c\n"
+ + " 1:1:void method():42:42 -> d";
+
+ private final String bMapping =
+ "foo -> foo:\n"
+ + " # {'id':'sourceFile','fileName':'Foo-inlinee.kt'}\n"
+ + "com.android.google.R8 -> b:\n"
+ + " # {'id':'sourceFile','fileName':'R8Car.kt'}\n"
+ + " 2:2:int foo.inlinee():43:43 -> f\n"
+ + " 2:2:int otherMethod():44 -> f\n"
+ + "# otherCommentHere";
+
+ private final String exceptionMapping = "com.android.google.exception -> exception:\n";
+
+ private final List<String> stackTrace =
+ Arrays.asList("exception: Hello World!", " at a.d(Unknown:1)", " at b.f(Unknown:2)");
+
+ private final List<String> expectedRetracedStackTrace =
+ Arrays.asList(
+ "com.android.google.exception: Hello World!",
+ " at com.foo.bar.baz.method(BarBaz.kt:42)",
+ " at foo.inlinee(Foo-inlinee.kt:43)",
+ " at com.android.google.R8.otherMethod(R8Car.kt:44)");
+
+ @Test
+ public void test() throws IOException {
+ List<String> preFetch = new ArrayList<>();
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.noMetadataBuilder(MapVersion.MAP_VERSION_1_0)
+ .setMappingPartitionFromKeySupplier(
+ key -> {
+ switch (key) {
+ case "a":
+ return aMapping.getBytes();
+ case "b":
+ return bMapping.getBytes();
+ case "exception":
+ return exceptionMapping.getBytes();
+ default:
+ fail();
+ return null;
+ }
+ })
+ .setRegisterMappingPartitionCallback(preFetch::add)
+ .build();
+ Retrace.run(
+ RetraceCommand.builder()
+ .setStackTrace(stackTrace)
+ .setMappingSupplier(mappingSupplier)
+ .setRetracedStackTraceConsumer(
+ retraced -> assertEquals(expectedRetracedStackTrace, retraced))
+ .build());
+ assertEquals(3, preFetch.size());
+ Set<String> expected = new HashSet<>(Arrays.asList("a", "b", "exception"));
+ assertEquals(expected, new HashSet<>(preFetch));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
new file mode 100644
index 0000000..3bc2eb2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
@@ -0,0 +1,117 @@
+// 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.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+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.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.Retracer;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionRoundTripInlineTest extends RetraceApiTestBase {
+
+ public RetracePartitionRoundTripInlineTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final ClassReference callerOriginal = Reference.classFromTypeName("some.Class");
+ private final ClassReference callerRenamed = Reference.classFromTypeName("b");
+
+ private final String mapping =
+ "# { id: 'com.android.tools.r8.mapping', version: '2.0' }\n"
+ + "inlinee.Class -> inlinee.Class:\n"
+ + " # {'id':'sourceFile','fileName':'InlineeClass.kt'}\n"
+ + callerOriginal.getTypeName()
+ + " -> "
+ + callerRenamed.getTypeName()
+ + ":\n"
+ + " # {'id':'sourceFile','fileName':'CallerClass.kt'}\n"
+ + " 1:1:void inlinee.Class.bar():42:42 -> a\n"
+ + " 1:1:void foo():43 -> a\n";
+
+ private int prepareCounter = 0;
+
+ @Test
+ public void test() throws IOException {
+ ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
+ Map<String, byte[]> partitions = new HashMap<>();
+ MappingPartitionMetadata metadataData =
+ ProguardMapPartitioner.builder(new DiagnosticsHandler() {})
+ .setProguardMapProducer(proguardMapProducer)
+ .setPartitionConsumer(
+ partition -> partitions.put(partition.getKey(), partition.getPayload()))
+ .build()
+ .run();
+ assertNotNull(metadataData);
+ assertEquals(2, partitions.size());
+
+ Set<String> preFetchedKeys = new LinkedHashSet<>();
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.builder()
+ .setMetadata(metadataData.getBytes())
+ .setRegisterMappingPartitionCallback(preFetchedKeys::add)
+ .setPrepareMappingPartitionsCallback(() -> prepareCounter++)
+ .setMappingPartitionFromKeySupplier(
+ key -> {
+ assertTrue(preFetchedKeys.contains(key));
+ assertTrue(partitions.containsKey(key));
+ return partitions.get(key);
+ })
+ .build();
+ assertEquals(0, prepareCounter);
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
+ List<RetraceFrameElement> callerRetraced =
+ retracer
+ .retraceFrame(
+ RetraceStackTraceContext.empty(),
+ OptionalInt.of(1),
+ Reference.methodFromDescriptor(callerRenamed, "a", "()V"))
+ .stream()
+ .collect(Collectors.toList());
+ // The retrace result should not be ambiguous or empty.
+ assertEquals(1, callerRetraced.size());
+ assertEquals(1, preFetchedKeys.size());
+ assertEquals(1, prepareCounter);
+ RetraceFrameElement retraceFrameElement = callerRetraced.get(0);
+
+ // Check that visiting all frames report all source files.
+ List<String> allSourceFiles =
+ retraceFrameElement.stream()
+ .map(x -> x.getSourceFile().getOrInferSourceFile())
+ .collect(Collectors.toList());
+ assertEquals(Arrays.asList("InlineeClass.kt", "CallerClass.kt"), allSourceFiles);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
new file mode 100644
index 0000000..0eac918
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripTest.java
@@ -0,0 +1,155 @@
+// 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.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+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.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetraceFrameElement;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.RetracedSingleFrame;
+import com.android.tools.r8.retrace.Retracer;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionRoundTripTest extends RetraceApiTestBase {
+
+ public RetracePartitionRoundTripTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final ClassReference outlineRenamed = Reference.classFromTypeName("a");
+ private final ClassReference callsiteOriginal = Reference.classFromTypeName("some.Class");
+ private final ClassReference callsiteRenamed = Reference.classFromTypeName("b");
+
+ private final String mapping =
+ "# { id: 'com.android.tools.r8.mapping', version: '2.0' }\n"
+ + "outline.Class -> "
+ + outlineRenamed.getTypeName()
+ + ":\n"
+ + " 1:2:int outline():0:0 -> a\n"
+ + "# { 'id':'com.android.tools.r8.outline' }\n"
+ + callsiteOriginal.getTypeName()
+ + " -> "
+ + callsiteRenamed.getTypeName()
+ + ":\n"
+ + " 1:1:void foo.bar.Baz.qux():42:42 -> s\n"
+ + " 4:4:int foo.bar.baz.outlineCaller(int):98:98 -> s\n"
+ + " 4:4:int outlineCaller(int):24 -> s\n"
+ + " 27:27:int outlineCaller(int):0:0 -> s\n" // This is the actual call to the outline
+ + "# { 'id':'com.android.tools.r8.outlineCallsite', "
+ + " 'positions': { '1': 4, '2': 5 } }\n";
+
+ private int prepareCounter = 0;
+
+ @Test
+ public void test() throws IOException {
+ ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
+ Map<String, byte[]> partitions = new HashMap<>();
+ MappingPartitionMetadata metadataData =
+ ProguardMapPartitioner.builder(new DiagnosticsHandler() {})
+ .setProguardMapProducer(proguardMapProducer)
+ .setPartitionConsumer(
+ partition -> partitions.put(partition.getKey(), partition.getPayload()))
+ .build()
+ .run();
+ assertNotNull(metadataData);
+ assertEquals(2, partitions.size());
+
+ Set<String> preFetchedKeys = new LinkedHashSet<>();
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.builder()
+ .setMetadata(metadataData.getBytes())
+ .setRegisterMappingPartitionCallback(preFetchedKeys::add)
+ .setPrepareMappingPartitionsCallback(() -> prepareCounter++)
+ .setMappingPartitionFromKeySupplier(
+ key -> {
+ assertTrue(preFetchedKeys.contains(key));
+ return partitions.get(key);
+ })
+ .build();
+ assertEquals(0, prepareCounter);
+ Retracer retracer = Retracer.builder().setMappingSupplier(mappingSupplier).build();
+ List<RetraceFrameElement> outlineRetraced =
+ retracer
+ .retraceFrame(
+ RetraceStackTraceContext.empty(),
+ OptionalInt.of(1),
+ Reference.methodFromDescriptor(outlineRenamed, "a", "()I"))
+ .stream()
+ .collect(Collectors.toList());
+ // The retrace result should not be ambiguous or empty.
+ assertEquals(1, outlineRetraced.size());
+ assertEquals(1, preFetchedKeys.size());
+ assertEquals(1, prepareCounter);
+ RetraceFrameElement retraceFrameElement = outlineRetraced.get(0);
+
+ // Check that visiting all frames report the outline.
+ List<RetracedMethodReference> allMethodReferences =
+ retraceFrameElement.stream()
+ .map(RetracedSingleFrame::getMethodReference)
+ .collect(Collectors.toList());
+ assertEquals(1, allMethodReferences.size());
+ assertEquals(0, allMethodReferences.get(0).getOriginalPositionOrDefault(2));
+
+ // Check that visiting rewritten frames will not report anything.
+ List<RetracedMethodReference> rewrittenReferences =
+ retraceFrameElement
+ .streamRewritten(RetraceStackTraceContext.empty())
+ .map(RetracedSingleFrame::getMethodReference)
+ .collect(Collectors.toList());
+ assertEquals(0, rewrittenReferences.size());
+
+ // Retrace the outline position
+ RetraceStackTraceContext context = retraceFrameElement.getRetraceStackTraceContext();
+ List<RetraceFrameElement> retraceOutlineCallee =
+ retracer
+ .retraceFrame(
+ context,
+ OptionalInt.of(27),
+ Reference.methodFromDescriptor(callsiteRenamed, "s", "(I)V"))
+ .stream()
+ .collect(Collectors.toList());
+ assertEquals(1, retraceOutlineCallee.size());
+ assertEquals(partitions.keySet(), preFetchedKeys);
+ assertEquals(2, prepareCounter);
+
+ List<RetracedMethodReference> outlineCallSiteFrames =
+ retraceOutlineCallee.get(0).stream()
+ .map(RetracedSingleFrame::getMethodReference)
+ .collect(Collectors.toList());
+ assertEquals(2, outlineCallSiteFrames.size());
+ assertEquals(98, outlineCallSiteFrames.get(0).getOriginalPositionOrDefault(27));
+ assertEquals(24, outlineCallSiteFrames.get(1).getOriginalPositionOrDefault(27));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java
new file mode 100644
index 0000000..58b092b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionSerializedObfuscatedKeyTest.java
@@ -0,0 +1,71 @@
+// 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.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionSerializedObfuscatedKeyTest extends RetraceApiTestBase {
+
+ public RetracePartitionSerializedObfuscatedKeyTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final String mapping = "com.R8 -> a:\n" + " void m() -> b\n";
+
+ // Serialized data from ObfuscatedTypeNameAsKeyMetadata with Map Version 1.0.
+ private final byte[] metadata = new byte[] {0, 0, 49, 46, 48};
+
+ private static final String minifiedStackTraceLine = " at a.b()";
+
+ private static final String retracedStackTraceLine = " at com.R8.m(R8.java)";
+
+ @Test
+ public void test() throws IOException {
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.builder()
+ .setMetadata(metadata)
+ .setMappingPartitionFromKeySupplier(
+ key -> {
+ assertEquals("a", key);
+ return mapping.getBytes(StandardCharsets.UTF_8);
+ })
+ .build();
+
+ List<String> stackTrace = new ArrayList<>();
+ stackTrace.add(minifiedStackTraceLine);
+
+ Retrace.run(
+ RetraceCommand.builder()
+ .setMappingSupplier(mappingSupplier)
+ .setStackTrace(stackTrace)
+ .setRetracedStackTraceConsumer(
+ retraced -> {
+ assertEquals(1, retraced.size());
+ assertEquals(retracedStackTraceLine, retraced.get(0));
+ })
+ .build());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStringTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStringTest.java
new file mode 100644
index 0000000..016cf49
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStringTest.java
@@ -0,0 +1,73 @@
+// 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.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionStringTest extends RetraceApiTestBase {
+
+ public RetracePartitionStringTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ private final String aMapping =
+ "com.foo.bar.baz -> a:\n"
+ + "# someCommentHere\n"
+ + " int field -> c\n"
+ + " void method() -> d";
+
+ private final String bMapping =
+ "com.android.google.r8 -> b:\n"
+ + " boolean otherField -> e\n"
+ + " int otherMethod() -> f\n"
+ + "# otherCommentHere";
+
+ private final String header = "# { id: 'com.android.tools.r8.mapping', version: '2.0' }";
+
+ private final String mapping = header + "\n" + aMapping + "\n" + bMapping;
+
+ @Test
+ public void test() throws IOException {
+ ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
+ Map<String, String> partitions = new HashMap<>();
+ MappingPartitionMetadata metadataData =
+ ProguardMapPartitioner.builder(new DiagnosticsHandler() {})
+ .setProguardMapProducer(proguardMapProducer)
+ .setPartitionConsumer(
+ partition ->
+ partitions.put(
+ partition.getKey(),
+ new String(partition.getPayload(), StandardCharsets.UTF_8)))
+ .build()
+ .run();
+ assertNotNull(metadataData);
+ assertEquals(2, partitions.size());
+ assertEquals(aMapping, partitions.get("a"));
+ assertEquals(bMapping, partitions.get("b"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 369d584..6df7b0a 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -10,18 +10,15 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
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.graph.DexProgramClass;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -211,14 +208,10 @@
.addKeepMainRule(R8.class)
.addKeepClassRules(CLASS_WITH_ANNOTATED_METHOD)
.addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
.apply(this::configureHorizontalClassMerging)
+ .apply(this::suppressZipFileAssignmentsToJavaLangAutoCloseable)
.compile()
.graphInspector();
@@ -235,12 +228,8 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
+ .apply(this::suppressZipFileAssignmentsToJavaLangAutoCloseable)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector);
@@ -258,12 +247,8 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
+ .apply(this::suppressZipFileAssignmentsToJavaLangAutoCloseable)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifThenKeepClassesWithMembersInspector);
@@ -283,12 +268,8 @@
+ " <2> <3>(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
+ .apply(this::suppressZipFileAssignmentsToJavaLangAutoCloseable)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
@@ -334,10 +315,11 @@
notInConditional);
}
- private void assertAllClassesAreSynthetics(Set<String> classNames) {
- for (String className : classNames) {
- ClassReference classReference = Reference.classFromTypeName(className);
- assertTrue(className, SyntheticItemsTestUtils.isExternalSynthetic(classReference));
- }
+ private void suppressZipFileAssignmentsToJavaLangAutoCloseable(R8TestBuilder<?> testBuilder) {
+ testBuilder.addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumArrayInAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumArrayInAnnotationTest.java
new file mode 100644
index 0000000..42bda91
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumArrayInAnnotationTest.java
@@ -0,0 +1,197 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.enums;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.TestState;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.BeforeClass;
+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 EnumArrayInAnnotationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean useGenericEnumsRule;
+
+ @Parameters(name = "{0}, use generic enums rule {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("TEST_ONE", "TEST_TWO", "TEST_THREE");
+
+ public static Path r8cf;
+ public static Box<String> minifiedEnumName = new Box<>();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ r8cf =
+ R8FullTestBuilder.create(new TestState(getStaticTemp()), Backend.CF)
+ .addInnerClasses(EnumArrayInAnnotationTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .addKeepRuntimeVisibleAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Enum.class), isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_ONE"),
+ isPresentAndNotRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_TWO"),
+ isPresentAndNotRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_THREE"),
+ isPresentAndRenamed());
+ minifiedEnumName.set(inspector.clazz(Enum.class).getFinalName());
+ })
+ .writeToZip();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ assumeTrue(useGenericEnumsRule);
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(!parameters.isDexRuntime() || useGenericEnumsRule);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .applyIf(
+ parameters.isCfRuntime() && useGenericEnumsRule,
+ TestShrinkerBuilder::addKeepEnumsRule,
+ parameters.isCfRuntime() && !useGenericEnumsRule,
+ builder ->
+ builder.addKeepRules(
+ "-keepclassmembernames enum "
+ + EnumArrayInAnnotationTest.Enum.class.getTypeName()
+ + " { <fields>; }"),
+ builder -> {
+ // Do nothing for DEX.
+ assertTrue(useGenericEnumsRule); // Check not running twice.
+ })
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_ONE"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_TWO"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_THREE"),
+ useGenericEnumsRule ? isPresentAndRenamed() : isPresentAndNotRenamed());
+ })
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8WithR8Input() throws Exception {
+ assumeTrue(useGenericEnumsRule);
+ testForR8(parameters.getBackend())
+ .addProgramFiles(r8cf)
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(minifiedEnumName.get()).uniqueFieldWithName("TEST_ONE"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(minifiedEnumName.get()).uniqueFieldWithName("TEST_TWO"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ })
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .applyIf(
+ useGenericEnumsRule,
+ TestShrinkerBuilder::addKeepEnumsRule,
+ builder ->
+ builder.addKeepRules(
+ "-keepclassmembernames enum " + Enum.class.getTypeName() + " { <fields>; }"))
+ .addDontWarn(getClass())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ !useGenericEnumsRule && parameters.asCfRuntime().isOlderThan(CfVm.JDK11),
+ r -> r.assertFailureWithErrorThatThrows(ArrayStoreException.class),
+ !useGenericEnumsRule && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11),
+ r -> r.assertFailureWithErrorThatThrows(EnumConstantNotPresentException.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT));
+ }
+
+ public enum Enum {
+ TEST_ONE,
+ TEST_TWO,
+ TEST_THREE
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface MyAnnotation {
+
+ Enum[] value();
+ }
+
+ @MyAnnotation(value = {Enum.TEST_ONE, Enum.TEST_TWO})
+ public static class Main {
+
+ public static void main(String[] args) {
+ for (Enum enm : Main.class.getAnnotation(MyAnnotation.class).value()) {
+ System.out.println(enm);
+ }
+ System.out.println(Enum.TEST_THREE);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
index 50f1954..5de0ff2 100644
--- a/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationTest.java
@@ -4,55 +4,159 @@
package com.android.tools.r8.shaking.enums;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.TestState;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.StringUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.BeforeClass;
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 EnumInAnnotationTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameter(1)
+ public boolean useGenericEnumsRule;
+
+ @Parameters(name = "{0}, use generic enums rule {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
- public EnumInAnnotationTest(TestParameters parameters) {
- this.parameters = parameters;
+ private static final String EXPECTED_RESULT = StringUtils.lines("TEST_ONE", "TEST_TWO");
+
+ public static Path r8cf;
+ public static Box<String> minifiedEnumName = new Box<>();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // Prepare a R8 processed JAR keeping Main with annotations and generic enum rule.
+ r8cf =
+ R8FullTestBuilder.create(new TestState(getStaticTemp()), Backend.CF)
+ .addInnerClasses(EnumInAnnotationTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .addKeepRuntimeVisibleAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Enum.class), isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_ONE"),
+ isPresentAndNotRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_TWO"),
+ isPresentAndRenamed());
+ minifiedEnumName.set(inspector.clazz(Enum.class).getFinalName());
+ })
+ .writeToZip();
}
@Test
public void testRuntime() throws Exception {
+ assumeTrue(useGenericEnumsRule);
testForRuntime(parameters)
- .addInnerClasses(EnumInAnnotationTest.class)
+ .addInnerClasses(getClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("TEST_ONE");
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
@Test
public void testR8() throws Exception {
+ assumeTrue(!parameters.isDexRuntime() || useGenericEnumsRule);
testForR8(parameters.getBackend())
.addInnerClasses(EnumInAnnotationTest.class)
.addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
.applyIf(
- parameters.isCfRuntime(),
+ parameters.isCfRuntime() && useGenericEnumsRule,
+ TestShrinkerBuilder::addKeepEnumsRule,
+ parameters.isCfRuntime() && !useGenericEnumsRule,
+ builder ->
+ builder.addKeepRules(
+ "-keepclassmembernames class "
+ + EnumInAnnotationTest.Enum.class.getTypeName()
+ + " { <fields>; }"),
+ builder -> {
+ // Do nothing for DEX.
+ assertTrue(useGenericEnumsRule); // Check not running twice.
+ })
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_ONE"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_TWO"),
+ useGenericEnumsRule ? isPresentAndRenamed() : isPresentAndNotRenamed());
+ })
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8WithR8Input() throws Exception {
+ assumeTrue(useGenericEnumsRule);
+ testForR8(parameters.getBackend())
+ .addProgramFiles(r8cf)
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(minifiedEnumName.get()).uniqueFieldWithName("TEST_ONE"),
+ parameters.isCfRuntime() ? isPresentAndNotRenamed() : isPresentAndRenamed());
+ })
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addInnerClasses(EnumInAnnotationTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .applyIf(
+ useGenericEnumsRule,
+ TestShrinkerBuilder::addKeepEnumsRule,
builder ->
builder.addKeepRules(
"-keepclassmembernames class " + Enum.class.getTypeName() + " { <fields>; }"))
- .setMinApi(parameters.getApiLevel())
+ .addDontWarn(getClass())
.addKeepRuntimeVisibleAnnotations()
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("TEST_ONE");
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
public enum Enum {
@@ -72,6 +176,7 @@
public static void main(String[] args) {
System.out.println(Main.class.getAnnotation(MyAnnotation.class).value());
+ System.out.println(Enum.TEST_TWO);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationWhereAnnotationIsDeadTest.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationWhereAnnotationIsDeadTest.java
new file mode 100644
index 0000000..bc2e7ac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumInAnnotationWhereAnnotationIsDeadTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.enums;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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 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;
+
+@RunWith(Parameterized.class)
+public class EnumInAnnotationWhereAnnotationIsDeadTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("TEST_ONE");
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepEnumsRule()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(Enum.class).uniqueFieldWithName("TEST_ONE"),
+ isPresentAndRenamed());
+ assertThat(inspector.clazz(Enum.class).uniqueFieldWithName("TEST_TWO"), isAbsent());
+ })
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ public enum Enum {
+ TEST_ONE,
+ TEST_TWO
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface MyAnnotation {
+
+ Enum value();
+ }
+
+ @MyAnnotation(value = Enum.TEST_ONE)
+ public static class ClassWithEnumAnnotation {}
+
+ public static class Main {
+
+ private static boolean alwaysFalse() {
+ return false;
+ }
+
+ public static void main(String[] args) {
+ if (alwaysFalse()) {
+ System.out.println(ClassWithEnumAnnotation.class.getAnnotation(MyAnnotation.class).value());
+ }
+ System.out.println(Enum.TEST_ONE);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 82d8fff..94bdbf1 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -47,11 +47,6 @@
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.addKeepRules(WHY_ARE_YOU_KEEPING_ALL)
- .addOptionsModification(
- options ->
- options
- .getOpenClosedInterfacesOptions()
- .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.collectStdout()
.compile()
.assertStdoutThatMatches(containsString("referenced in keep rule"))
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
index 20fda04..e7b5fe6 100644
--- a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -20,11 +20,19 @@
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 JumboStringTest extends SmaliTestBase {
private static Pair<StringBuilder, StringBuilder> builders;
- private final TestParameters parameters;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withOnlyDexRuntimeApiLevel().build();
+ }
@BeforeClass
public static void createBuilders() {
@@ -49,15 +57,6 @@
builders = new Pair<>(builder, expectedBuilder);
}
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
- }
-
- public JumboStringTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
SmaliBuilder smaliBuilder = new SmaliBuilder(DEFAULT_CLASS_NAME);
@@ -82,16 +81,21 @@
testForR8(parameters.getBackend())
.addProgramDexFileData(smaliBuilder.compile())
.addKeepMainRule(DEFAULT_CLASS_NAME)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> {
+ options.enableStringConcatenationOptimization = false;
+ })
.run(parameters.getRuntime(), DEFAULT_CLASS_NAME)
.assertSuccessWithOutput(builders.getSecond().toString())
- .inspect(inspector -> {
- ClassSubject main = inspector.clazz(DEFAULT_CLASS_NAME);
- assertThat(main, isPresent());
- MethodSubject method = main.uniqueMethodWithName(DEFAULT_METHOD_NAME);
- assertThat(method, isPresent());
- assertTrue(method.streamInstructions().anyMatch(InstructionSubject::isJumboString));
- });
+ .inspect(
+ inspector -> {
+ ClassSubject main = inspector.clazz(DEFAULT_CLASS_NAME);
+ assertThat(main, isPresent());
+ MethodSubject method = main.uniqueMethodWithName(DEFAULT_METHOD_NAME);
+ assertThat(method, isPresent());
+ assertTrue(method.streamInstructions().anyMatch(InstructionSubject::isJumboString));
+ });
}
@Test
@@ -128,18 +132,23 @@
.addProgramDexFileData(smaliBuilder.compile())
.addKeepMainRule(DEFAULT_CLASS_NAME)
.addKeepRules("-addconfigurationdebugging")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> {
+ options.enableStringConcatenationOptimization = false;
+ })
.run(parameters.getRuntime(), DEFAULT_CLASS_NAME)
.assertSuccessWithOutput(builders.getSecond().toString())
- .inspect(inspector -> {
- ClassSubject main = inspector.clazz(DEFAULT_CLASS_NAME);
- assertThat(main, isPresent());
- MethodSubject method = main.uniqueMethodWithName(DEFAULT_METHOD_NAME);
- assertThat(method, isPresent());
- assertTrue(method.streamInstructions().anyMatch(InstructionSubject::isJumboString));
- MethodSubject method2 = main.uniqueMethodWithName(DEFAULT_METHOD_NAME + "2");
- assertThat(method2, isPresent());
- assertTrue(method2.streamInstructions().anyMatch(InstructionSubject::isJumboString));
- });
+ .inspect(
+ inspector -> {
+ ClassSubject main = inspector.clazz(DEFAULT_CLASS_NAME);
+ assertThat(main, isPresent());
+ MethodSubject method = main.uniqueMethodWithName(DEFAULT_METHOD_NAME);
+ assertThat(method, isPresent());
+ assertTrue(method.streamInstructions().anyMatch(InstructionSubject::isJumboString));
+ MethodSubject method2 = main.uniqueMethodWithName(DEFAULT_METHOD_NAME + "2");
+ assertThat(method2, isPresent());
+ assertTrue(method2.streamInstructions().anyMatch(InstructionSubject::isJumboString));
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 10d96c1..56435ed 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -950,10 +950,7 @@
for (int i = 1; i < count; i++) {
// Build a new application with the Outliner class.
originalApplication = processedApplication;
- processedApplication =
- processApplication(
- originalApplication,
- options.andThen(o -> o.testing.allowConflictingSyntheticTypes = true));
+ processedApplication = processApplication(originalApplication, options);
assertEquals((i + 1) * 3, getNumberOfProgramClasses(processedApplication));
}
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
deleted file mode 100644
index 67e965d..0000000
--- a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
+++ /dev/null
@@ -1,176 +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.smali;
-
-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.assertTrue;
-
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-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 RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
-
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
- }
-
- public RemoveWriteOfUnusedFieldsTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void unreadStaticFieldsRemoved() throws Exception {
- SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
- // All these static fields are set but never read.
- builder.addStaticField("booleanField", "Z");
- builder.addStaticField("byteField", "B");
- builder.addStaticField("shortField", "S");
- builder.addStaticField("intField", "I");
- builder.addStaticField("longField", "J");
- builder.addStaticField("floatField", "F");
- builder.addStaticField("doubleField", "D");
- builder.addStaticField("charField", "C");
- builder.addStaticField("objectField", "Ljava/lang/Object;");
- builder.addStaticField("stringField", "Ljava/lang/String;");
- builder.addStaticField("testField", "LTest;");
-
- builder.addStaticMethod("void", "test", ImmutableList.of(),
- 2,
- "const v0, 0", // single non-float typed zero (ie, int type)
- "sput-boolean v0, LTest;->booleanField:Z",
- "sput-byte v0, LTest;->byteField:B",
- "sput-char v0, LTest;->charField:C",
- "sput-short v0, LTest;->shortField:S",
- "sput v0, LTest;->intField:I",
- "const v0, 0", // float typed zero
- "sput v0, LTest;->floatField:F",
- "const v0, 0", // reference typed null
- "sput-object v0, LTest;->objectField:Ljava/lang/Object;",
- "sput-object v0, LTest;->stringField:Ljava/lang/String;",
- "sput-object v0, LTest;->testField:LTest;",
- "const-wide v0, 0", // wide typed long
- "sput-wide v0, LTest;->longField:J",
- "const-wide v0, 0", // wide typed double
- "sput-wide v0, LTest;->doubleField:D",
- "return-void");
-
- builder.addMainMethod(
- 0,
- " invoke-static { }, LTest;->test()V",
- " return-void ");
-
- AndroidApp input =
- AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
-
- Path inputPath = temp.getRoot().toPath().resolve("input.zip");
- input.writeToZip(inputPath, OutputMode.DexIndexed);
- ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
-
- testForR8(parameters.getBackend())
- .addProgramFiles(inputPath)
- .addKeepMainRule("Test")
- .setMinApi(parameters.getRuntime())
- .compile()
- .inspect(
- inspector -> {
- MethodSubject method =
- inspector.clazz("Test").method("void", "test", ImmutableList.of());
- assertThat(
- "Expected method to be removed entirely because it does not have side effects",
- method,
- not(isPresent()));
- });
- }
-
- @Test
- public void unreadInstanceFieldsRemoved() throws Exception {
- SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
-
- // All these instance fields are set but never read.
- builder.addInstanceField("booleanField", "Z");
- builder.addInstanceField("byteField", "B");
- builder.addInstanceField("shortField", "S");
- builder.addInstanceField("intField", "I");
- builder.addInstanceField("longField", "J");
- builder.addInstanceField("floatField", "F");
- builder.addInstanceField("doubleField", "D");
- builder.addInstanceField("charField", "C");
- builder.addInstanceField("objectField", "Ljava/lang/Object;");
- builder.addInstanceField("stringField", "Ljava/lang/String;");
- builder.addInstanceField("testField", "LTest;");
-
- builder.addInstanceMethod("void", "test", ImmutableList.of(),
- 2,
- "const v0, 0",
- "iput-boolean v0, p0, LTest;->booleanField:Z",
- "iput-byte v0, p0, LTest;->byteField:B",
- "iput-char v0, p0, LTest;->charField:C",
- "iput-short v0, p0, LTest;->shortField:S",
- "iput v0, p0, LTest;->intField:I",
- "const v0, 0",
- "iput v0, p0, LTest;->floatField:F",
- "const v0, 0",
- "iput-object v0, p0, LTest;->objectField:Ljava/lang/Object;",
- "iput-object v0, p0, LTest;->stringField:Ljava/lang/String;",
- "iput-object v0, p0, LTest;->testField:LTest;",
- "const-wide v0, 0",
- "iput-wide v0, p0, LTest;->longField:J",
- "const-wide v0, 0",
- "iput-wide v0, p0, LTest;->doubleField:D",
- "return-void");
-
- builder.addInitializer(ImmutableList.of(), 0,
- "invoke-direct {p0}, Ljava/lang/Object;-><init>()V",
- "return-void"
- );
-
- builder.addMainMethod(
- 1,
- " new-instance v0, LTest;",
- " invoke-direct { v0 }, LTest;-><init>()V",
- " invoke-virtual { v0 }, LTest;->test()V",
- " return-void ");
-
- AndroidApp input =
- AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
-
- Path inputPath = temp.getRoot().toPath().resolve("input.zip");
- input.writeToZip(inputPath, OutputMode.DexIndexed);
- ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
-
- testForR8(parameters.getBackend())
- .addProgramFiles(inputPath)
- .addKeepMainRule("Test")
- .addKeepRules("-keep class Test { void test(); }")
- .setMinApi(parameters.getRuntime())
- .compile()
- .inspect(
- inspector -> {
- MethodSubject method =
- inspector.clazz("Test").method("void", "test", ImmutableList.of());
- DexCode code = method.getMethod().getCode().asDexCode();
- assertTrue(code.isEmptyVoidMethod());
- });
- }
-}
diff --git a/third_party/opensource-apps/systemui.tar.gz.sha1 b/third_party/opensource-apps/systemui.tar.gz.sha1
new file mode 100644
index 0000000..9d5b06c
--- /dev/null
+++ b/third_party/opensource-apps/systemui.tar.gz.sha1
@@ -0,0 +1 @@
+19afeb80f13ccaadddd62d9e1f8f2b1e894f99c8
\ 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 abfaa5e..da20d2e 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-29e9654bef01ef4e00b0e44849b9073a02997b8b
\ No newline at end of file
+4408d86adfb21f32c2dcb93c2c8f2e392f0e0de0
\ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 2f13541..10b300a 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -29,7 +29,8 @@
import sys
import utils
-VERSION_FILE = 'VERSION_JDK11.txt'
+VERSION_FILE_JDK8 = 'VERSION.txt'
+VERSION_FILE_JDK11 = 'VERSION_JDK11.txt'
LIBRARY_NAME = 'desugar_jdk_libs'
def ParseOptions(argv):
@@ -170,7 +171,10 @@
with utils.TempDir() as checkout_dir:
CloneDesugaredLibrary(options.github_account, checkout_dir)
- version = GetVersion(os.path.join(checkout_dir, VERSION_FILE))
+ version = GetVersion(
+ os.path.join(
+ checkout_dir,
+ VERSION_FILE_JDK11 if variant == 'jdk11' else VERSION_FILE_JDK8))
destination = archive.GetVersionDestination(
'gs://', LIBRARY_NAME + '/' + version, is_main)
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 5962cc0..c6a4a19 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -357,19 +357,33 @@
return True
return False
-def prepare_wrapper(dist, temp, jdkhome):
- wrapper_file = os.path.join(
+def prepare_r8_wrapper(dist, temp, jdkhome):
+ compile_with_javac(
+ dist,
+ temp,
+ jdkhome,
+ os.path.join(
utils.REPO_ROOT,
- 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
+ 'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java'))
+
+def prepare_d8_wrapper(dist, temp, jdkhome):
+ compile_with_javac(
+ dist,
+ temp,
+ jdkhome,
+ os.path.join(
+ utils.REPO_ROOT,
+ 'src/main/java/com/android/tools/r8/utils/CompileDumpD8.java'))
+
+def compile_with_javac(dist, temp, jdkhome, path):
cmd = [
jdk.GetJavacExecutable(jdkhome),
- wrapper_file,
+ path,
'-d', temp,
'-cp', dist,
]
utils.PrintCmd(cmd)
subprocess.check_output(cmd)
- return temp
def is_hash(version):
return len(version) == 40
@@ -396,7 +410,8 @@
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)
- wrapper_dir = prepare_wrapper(jar, temp, jdkhome)
+ prepare_r8_wrapper(jar, temp, jdkhome)
+ prepare_d8_wrapper(jar, temp, jdkhome)
cmd = [jdk.GetJavaExecutable(jdkhome)]
if args.debug_agent:
if not args.nolib:
@@ -415,9 +430,9 @@
if hasattr(args, 'properties'):
cmd.extend(args.properties);
cmd.extend(determine_properties(build_properties))
- cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
+ cmd.extend(['-cp', '%s:%s' % (temp, jar)])
if compiler == 'd8':
- cmd.append('com.android.tools.r8.D8')
+ cmd.append('com.android.tools.r8.utils.CompileDumpD8')
if compiler == 'l8':
cmd.append('com.android.tools.r8.L8')
if compiler.startswith('r8'):
diff --git a/tools/git_utils.py b/tools/git_utils.py
index 1e3e7c5..1ce0a0a 100644
--- a/tools/git_utils.py
+++ b/tools/git_utils.py
@@ -16,4 +16,4 @@
cmd = ['git', 'rev-parse', revision_from]
utils.PrintCmd(cmd)
with utils.ChangedWorkingDirectory(checkout_dir):
- return subprocess.check_output(cmd).strip()
+ return subprocess.check_output(cmd).strip().decode('utf-8')
diff --git a/tools/trigger.py b/tools/trigger.py
index d467a81..d87cad9 100755
--- a/tools/trigger.py
+++ b/tools/trigger.py
@@ -22,7 +22,8 @@
# triggers: "BUILDER_NAME"
TRIGGERS_RE = r'^ triggers: "(\w.*)"'
-DESUGAR_BOT = 'lib_desugar-archive'
+DESUGAR_JDK11_BOT = 'lib_desugar-archive-jdk11'
+DESUGAR_JDK8_BOT = 'lib_desugar-archive-jdk8'
def ParseOptions():
result = optparse.OptionParser()
@@ -33,9 +34,13 @@
help='Run the specified cl on the bots. This should be '
'the full url, e.g., '
'https://r8-review.googlesource.com/c/r8/+/37420/1')
- result.add_option('--desugar',
- help='Run the library desugar and archiving bot.',
+ result.add_option('--desugar-jdk11',
+ help='Run the jdk11 library desugar and archiving bot.',
default=False, action='store_true')
+ result.add_option('--desugar-jdk8',
+ help='Run the jdk8 library desugar and archiving bot.',
+ default=False, action='store_true')
+
result.add_option('--builder', help='Trigger specific builder')
return result.parse_args()
@@ -60,7 +65,8 @@
else:
assert 'release' not in builder, builder
main_builders.append(builder)
- print('Desugar builder:\n ' + DESUGAR_BOT)
+ print('Desugar jdk11 builder:\n ' + DESUGAR_JDK11_BOT)
+ print('Desugar jdk8 builder:\n ' + DESUGAR_JDK8_BOT)
print('Main builders:\n ' + '\n '.join(main_builders))
print('Release builders:\n ' + '\n '.join(release_builders))
return (main_builders, release_builders)
@@ -84,7 +90,8 @@
def Main():
(options, args) = ParseOptions()
- if len(args) != 1 and not options.cl and not options.desugar:
+ desugar = options.desugar_jdk11 or options.desugar_jdk8
+ if len(args) != 1 and not options.cl and not desugar:
print('Takes exactly one argument, the commit to run')
return 1
@@ -92,19 +99,20 @@
print('You can\'t run cls on the release bots')
return 1
- if options.cl and options.desugar:
+ if options.cl and desugar:
print('You can\'t run cls on the desugar bot')
return 1
- commit = None if (options.cl or options.desugar) else args[0]
+ commit = None if (options.cl or desugar) else args[0]
(main_builders, release_builders) = get_builders()
builders = release_builders if options.release else main_builders
if options.builder:
builder = options.builder
assert builder in main_builders or builder in release_builders
builders = [options.builder]
- if options.desugar:
- builders = [DESUGAR_BOT]
+ if desugar:
+ assert options.desugar_jdk8 or options.desugar_jdk11
+ builders = [DESUGAR_JDK8_BOT if options.desugar_jdk8 else DESUGAR_JDK11_BOT]
commit = git_utils.GetHeadRevision(utils.REPO_ROOT, use_main=True)
if options.cl:
trigger_cl(builders, options.cl)
diff --git a/tools/utils.py b/tools/utils.py
index 363cc18..cd41ad0 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -66,7 +66,7 @@
R8LIB_TESTS_DEPS_JAR = R8_TESTS_DEPS_JAR
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
-LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')
+LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.jar')
DESUGAR_CONFIGURATION = os.path.join(
'src', 'library_desugar', 'desugar_jdk_libs.json')