Merge commit '9c99831edeabd91eecfc4ee68aa0f8e51e54b1ac' into dev-release
diff --git a/build.gradle b/build.gradle
index 2edec91..fba205d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -90,6 +90,12 @@
}
output.resourcesDir = 'build/classes/cfSegments'
}
+ libraryDesugarConversions {
+ java {
+ srcDirs = ['src/library_desugar/java']
+ }
+ output.resourcesDir = 'build/classes/library_desugar_conversions'
+ }
debugTestResources {
java {
srcDirs = ['src/test/debugTestResources']
@@ -566,6 +572,7 @@
setJava11Compilation(sourceSets.jdk11TimeTests.compileJavaTaskName)
task compileMainWithJava11 (type: JavaCompile) {
+ dependsOn downloadDeps
def jdkDir = 'third_party/openjdk/jdk-11/'
options.fork = true
options.forkOptions.jvmArgs = []
@@ -851,7 +858,14 @@
}
}
-task testJar(type: ShadowJar, dependsOn: testClasses) {
+task buildLibraryDesugarConversions(type: Zip, dependsOn: downloadDeps) {
+ from sourceSets.libraryDesugarConversions.output
+ include "java/**/*.class"
+ baseName 'library_desugar_conversions'
+ destinationDir file('build/libs')
+}
+
+task testJar(type: ShadowJar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
baseName = "r8tests"
from sourceSets.test.output
// We only want to include tests that use R8 when generating keep rules for applymapping.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json
rename to src/library_desugar/desugar_jdk_libs.json
index a281c3d..3cfd647 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,7 +1,8 @@
{
- "configuration_format_version": 2,
- "version": "0.8.0",
+ "configuration_format_version": 3,
+ "version": "0.9.0",
"required_compilation_api_level": 26,
+ "synthesized_library_classes_package_prefix": "j$.",
"library_flags": [
{
"api_level_below_or_equal": 25,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs_comments.md b/src/library_desugar/desugar_jdk_libs_comments.md
similarity index 100%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs_comments.md
rename to src/library_desugar/desugar_jdk_libs_comments.md
diff --git a/src/test/desugaredLibrary/stubs/timestubs/Duration.java b/src/library_desugar/java/j$/time/Duration.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/Duration.java
rename to src/library_desugar/java/j$/time/Duration.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/Instant.java b/src/library_desugar/java/j$/time/Instant.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/Instant.java
rename to src/library_desugar/java/j$/time/Instant.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/LocalDate.java b/src/library_desugar/java/j$/time/LocalDate.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/LocalDate.java
rename to src/library_desugar/java/j$/time/LocalDate.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/MonthDay.java b/src/library_desugar/java/j$/time/MonthDay.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/MonthDay.java
rename to src/library_desugar/java/j$/time/MonthDay.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/ZoneId.java b/src/library_desugar/java/j$/time/ZoneId.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/ZoneId.java
rename to src/library_desugar/java/j$/time/ZoneId.java
diff --git a/src/test/desugaredLibrary/stubs/timestubs/ZonedDateTime.java b/src/library_desugar/java/j$/time/ZonedDateTime.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/timestubs/ZonedDateTime.java
rename to src/library_desugar/java/j$/time/ZonedDateTime.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/Optional.java b/src/library_desugar/java/j$/util/optionalstubs/Optional.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/Optional.java
rename to src/library_desugar/java/j$/util/optionalstubs/Optional.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalDouble.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalDouble.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalDouble.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalDouble.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalInt.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalInt.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalInt.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalInt.java
diff --git a/src/test/desugaredLibrary/stubs/optionalstubs/OptionalLong.java b/src/library_desugar/java/j$/util/optionalstubs/OptionalLong.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/optionalstubs/OptionalLong.java
rename to src/library_desugar/java/j$/util/optionalstubs/OptionalLong.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/DoubleSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/DoubleSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/DoubleSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/DoubleSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/IntSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/IntSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/IntSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/IntSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/stubs/summarystatisticsstubs/LongSummaryStatistics.java b/src/library_desugar/java/j$/util/summarystatisticsstubs/LongSummaryStatistics.java
similarity index 100%
rename from src/test/desugaredLibrary/stubs/summarystatisticsstubs/LongSummaryStatistics.java
rename to src/library_desugar/java/j$/util/summarystatisticsstubs/LongSummaryStatistics.java
diff --git a/src/test/desugaredLibrary/conversions/TimeConversions.java b/src/library_desugar/java/java/time/TimeConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/TimeConversions.java
rename to src/library_desugar/java/java/time/TimeConversions.java
diff --git a/src/test/desugaredLibrary/conversions/DoubleSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/DoubleSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/DoubleSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/IntSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/IntSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/IntSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/LongSummaryStatisticsConversions.java b/src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/LongSummaryStatisticsConversions.java
rename to src/library_desugar/java/java/util/LongSummaryStatisticsConversions.java
diff --git a/src/test/desugaredLibrary/conversions/OptionalConversions.java b/src/library_desugar/java/java/util/OptionalConversions.java
similarity index 100%
rename from src/test/desugaredLibrary/conversions/OptionalConversions.java
rename to src/library_desugar/java/java/util/OptionalConversions.java
diff --git a/src/main/java/com/android/tools/r8/CompatDexHelper.java b/src/main/java/com/android/tools/r8/CompatDexHelper.java
deleted file mode 100644
index fa52e25..0000000
--- a/src/main/java/com/android/tools/r8/CompatDexHelper.java
+++ /dev/null
@@ -1,11 +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;
-
-public class CompatDexHelper {
- public static void ignoreDexInArchive(BaseCommand.Builder builder) {
- builder.setIgnoreDexInArchive(true);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
new file mode 100644
index 0000000..a84bab5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -0,0 +1,30 @@
+// 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 does not desugar.
+ options.enableDesugaring = false;
+ // 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/D8Logger.java b/src/main/java/com/android/tools/r8/D8Logger.java
index a3015dc..6c3fca0 100644
--- a/src/main/java/com/android/tools/r8/D8Logger.java
+++ b/src/main/java/com/android/tools/r8/D8Logger.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.compatdx.CompatDx;
import com.google.common.collect.ImmutableList;
import java.io.FileWriter;
import java.io.IOException;
@@ -17,7 +18,8 @@
"Usage: java -jar d8logger.jar <compiler-options>",
" where <compiler-options> will be",
"",
- " 1. forwarded to the 'd8' tool, and also",
+ " 1. forwarded to the 'd8' or 'compatdx' tool (depending on the presence of the '--dex'",
+ " option), and also",
" 2. appended to the file in the environment variable 'D8LOGGER_OUTPUT'",
"",
" The options will be appended as a new line with TAB characters between the arguments."));
@@ -41,6 +43,10 @@
}
}
- D8.main(args);
+ if (Arrays.stream(args).anyMatch(s -> s.equals("--dex"))) {
+ CompatDx.main(args);
+ } else {
+ D8.main(args);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 28713e3..50ea501 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -89,10 +89,7 @@
desugar(app, options, executorService);
});
if (shrink) {
- InternalOptions r8Options = r8Command.getInternalOptions();
- // Disable outlining for R8 when called from L8.
- r8Options.outline.enabled = false;
- R8.runForTesting(r8Command.getInputApp(), r8Options);
+ R8.run(r8Command);
} else {
D8.run(d8Command, executorService);
}
@@ -107,6 +104,9 @@
try {
// Disable global optimizations.
options.disableGlobalOptimizations();
+ // Since L8 Cf representation is temporary, just disable long running back-end optimizations
+ // on it.
+ options.enableLoadStoreOptimization = false;
DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
PrefixRewritingMapper rewritePrefix =
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 0d8f3ba..b906cb2 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -31,6 +32,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
@@ -70,6 +72,7 @@
private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
private Set<DexType> noObfuscationTypes = Sets.newIdentityHashSet();
+ private Set<String> keepPackageNames = Sets.newHashSet();
private final DexApplication application;
private final AppInfoWithSubtyping appInfo;
private int errors;
@@ -165,11 +168,12 @@
private void addType(DexType type) {
if (isTargetType(type) && types.add(type)) {
DexClass clazz = appInfo.definitionFor(type);
- if (clazz == null
- || clazz.accessFlags.isVisibilityDependingOnPackage()
- || !allowObfuscation) {
+ if (clazz == null || !allowObfuscation) {
noObfuscationTypes.add(type);
}
+ if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
+ keepPackageNames.add(clazz.type.getPackageName());
+ }
methods.put(type, Sets.newIdentityHashSet());
fields.put(type, Sets.newIdentityHashSet());
}
@@ -192,9 +196,12 @@
Set<DexField> typeFields = fields.get(field.holder);
if (typeFields != null) {
assert baseField != null;
- if (!allowObfuscation || baseField.accessFlags.isVisibilityDependingOnPackage()) {
+ if (!allowObfuscation) {
noObfuscationTypes.add(field.holder);
}
+ if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
+ keepPackageNames.add(baseField.field.holder.getPackageName());
+ }
typeFields.add(field);
}
}
@@ -208,10 +215,13 @@
Set<DexMethod> typeMethods = methods.get(method.holder);
if (typeMethods != null) {
DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
- assert encodedMethod != null;
- if (!allowObfuscation || encodedMethod.accessFlags.isVisibilityDependingOnPackage()) {
+ assert encodedMethod != null : "Could not find method " + method.toString();
+ if (!allowObfuscation) {
noObfuscationTypes.add(method.holder);
}
+ if (encodedMethod.accessFlags.isVisibilityDependingOnPackage()) {
+ keepPackageNames.add(encodedMethod.method.holder.getPackageName());
+ }
typeMethods.add(method);
}
}
@@ -344,7 +354,8 @@
}
private void print() {
- errors = printer.print(application, types, noObfuscationTypes, methods, fields);
+ errors =
+ printer.print(application, types, noObfuscationTypes, keepPackageNames, methods, fields);
}
private abstract static class Printer {
@@ -378,6 +389,8 @@
abstract void printMethod(DexEncodedMethod encodedMethod, String typeName);
+ abstract void printPackageNames(List<String> packageNames);
+
void printNameAndReturn(DexEncodedMethod encodedMethod) {
if (encodedMethod.accessFlags.isConstructor()) {
printConstructorName(encodedMethod);
@@ -397,6 +410,7 @@
DexApplication application,
Set<DexType> types,
Set<DexType> noObfuscationTypes,
+ Set<String> keepPackageNames,
Map<DexType, Set<DexMethod>> methods,
Map<DexType, Set<DexField>> fields) {
int errors = 0;
@@ -431,6 +445,9 @@
}
printTypeFooter();
}
+ ArrayList<String> packageNamesToKeep = new ArrayList<>(keepPackageNames);
+ Collections.sort(packageNamesToKeep);
+ printPackageNames(packageNamesToKeep);
return errors;
}
}
@@ -457,6 +474,11 @@
}
@Override
+ void printPackageNames(List<String> packageNames) {
+ // No need to print package names for text output.
+ }
+
+ @Override
void printTypeHeader(DexClass dexClass, boolean allowObfuscation) {
appendLine(dexClass.type.toSourceString());
}
@@ -520,6 +542,11 @@
}
@Override
+ void printPackageNames(List<String> packageNames) {
+ append("-keeppackagenames " + StringUtils.join(packageNames, ",") + "\n");
+ }
+
+ @Override
public void printTypeFooter() {
appendLine("}");
}
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index e352ede..f54233b 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.bisect.Bisect;
+import com.android.tools.r8.compatdx.CompatDx;
import com.android.tools.r8.compatproguard.CompatProguard;
import com.android.tools.r8.dexfilemerger.DexFileMerger;
import com.android.tools.r8.dexsplitter.DexSplitter;
@@ -30,6 +31,9 @@
case "bisect":
Bisect.main(shift(args));
break;
+ case "compatdx":
+ CompatDx.main(shift(args));
+ break;
case "compatproguard":
CompatProguard.main(shift(args));
break;
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index f7e6dc3..f4f3c7e 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.compatdexbuilder;
import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.CompatDexHelper;
+import com.android.tools.r8.CompatDxHelper;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
@@ -162,7 +162,7 @@
throws IOException, CompilationFailedException {
DexConsumer consumer = new DexConsumer();
D8Command.Builder builder = D8Command.builder();
- CompatDexHelper.ignoreDexInArchive(builder);
+ CompatDxHelper.ignoreDexInArchive(builder);
builder
.setProgramConsumer(consumer)
.setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG)
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
new file mode 100644
index 0000000..a52c2ea
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -0,0 +1,618 @@
+// 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.compatdx;
+
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.CompatDxHelper;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.Version;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Dx compatibility interface for d8.
+ *
+ * This should become a mostly drop-in replacement for uses of the DX dexer (eg, dx --dex ...).
+ */
+public class CompatDx {
+
+ private static final String USAGE_HEADER = "Usage: compatdx [options] <input files>";
+
+ /**
+ * Compatibility options parsing for the DX --dex sub-command.
+ */
+ public static class DxCompatOptions {
+ // Final values after parsing.
+ // Note: These are ordered by their occurrence in "dx --help"
+ public final boolean help;
+ public final boolean version;
+ public final boolean debug;
+ public final boolean verbose;
+ public final PositionInfo positions;
+ public final boolean noLocals;
+ public final boolean noOptimize;
+ public final boolean statistics;
+ public final String optimizeList;
+ public final String noOptimizeList;
+ public final boolean noStrict;
+ public final boolean keepClasses;
+ public final String output;
+ public final String dumpTo;
+ public final int dumpWidth;
+ public final String dumpMethod;
+ public final boolean verboseDump;
+ public final boolean dump;
+ public final boolean noFiles;
+ public final boolean coreLibrary;
+ public final int numThreads;
+ public final boolean incremental;
+ public final boolean forceJumbo;
+ public final boolean noWarning;
+ public final boolean multiDex;
+ public final String mainDexList;
+ public final boolean minimalMainDex;
+ public final int minApiLevel;
+ public final String inputList;
+ public final ImmutableList<String> inputs;
+ // Undocumented option
+ public final int maxIndexNumber;
+
+ private static final String FILE_ARG = "file";
+ private static final String NUM_ARG = "number";
+ private static final String METHOD_ARG = "method";
+
+ public enum PositionInfo {
+ NONE, IMPORTANT, LINES, THROWING
+ }
+
+ // Exception thrown on invalid dx compat usage.
+ public static class DxUsageMessage extends Exception {
+ public final String message;
+
+ DxUsageMessage(String message) {
+ this.message = message;
+ }
+
+ void printHelpOn(PrintStream sink) throws IOException {
+ sink.println(message);
+ }
+ }
+
+ // Parsing specification.
+ private static class Spec {
+ final OptionParser parser;
+
+ // Note: These are ordered by their occurrence in "dx --help"
+ final OptionSpec<Void> debug;
+ final OptionSpec<Void> verbose;
+ final OptionSpec<String> positions;
+ final OptionSpec<Void> noLocals;
+ final OptionSpec<Void> noOptimize;
+ final OptionSpec<Void> statistics;
+ final OptionSpec<String> optimizeList;
+ final OptionSpec<String> noOptimizeList;
+ final OptionSpec<Void> noStrict;
+ final OptionSpec<Void> keepClasses;
+ final OptionSpec<String> output;
+ final OptionSpec<String> dumpTo;
+ final OptionSpec<Integer> dumpWidth;
+ final OptionSpec<String> dumpMethod;
+ final OptionSpec<Void> dump;
+ final OptionSpec<Void> verboseDump;
+ final OptionSpec<Void> noFiles;
+ final OptionSpec<Void> coreLibrary;
+ final OptionSpec<Integer> numThreads;
+ final OptionSpec<Void> incremental;
+ final OptionSpec<Void> forceJumbo;
+ final OptionSpec<Void> noWarning;
+ final OptionSpec<Void> multiDex;
+ final OptionSpec<String> mainDexList;
+ final OptionSpec<Void> minimalMainDex;
+ final OptionSpec<Integer> minApiLevel;
+ final OptionSpec<String> inputList;
+ final OptionSpec<String> inputs;
+ final OptionSpec<Void> version;
+ final OptionSpec<Void> help;
+ final OptionSpec<Integer> maxIndexNumber;
+
+ Spec() {
+ parser = new OptionParser();
+ parser.accepts("dex");
+ debug = parser.accepts("debug", "Print debug information");
+ verbose = parser.accepts("verbose", "Print verbose information");
+ positions = parser
+ .accepts("positions",
+ "What source-position information to keep. One of: none, lines, important")
+ .withOptionalArg()
+ .describedAs("keep")
+ .defaultsTo("lines");
+ noLocals = parser.accepts("no-locals", "Don't keep local variable information");
+ statistics = parser.accepts("statistics", "Print statistics information");
+ noOptimize = parser.accepts("no-optimize", "Don't optimize");
+ optimizeList = parser
+ .accepts("optimize-list", "File listing methods to optimize")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ noOptimizeList = parser
+ .accepts("no-optimize-list", "File listing methods not to optimize")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ noStrict = parser.accepts("no-strict", "Disable strict file/class name checks");
+ keepClasses = parser.accepts("keep-classes", "Keep input class files in in output jar");
+ output = parser
+ .accepts("output", "Output file or directory")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ dumpTo = parser
+ .accepts("dump-to", "File to dump information to")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ dumpWidth = parser
+ .accepts("dump-width", "Max width for columns in dump output")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(0)
+ .describedAs(NUM_ARG);
+ dumpMethod = parser
+ .accepts("dump-method", "Method to dump information for")
+ .withRequiredArg()
+ .describedAs(METHOD_ARG);
+ dump = parser.accepts("dump", "Dump information");
+ verboseDump = parser.accepts("verbose-dump", "Dump verbose information");
+ noFiles = parser.accepts("no-files", "Don't fail if given no files");
+ coreLibrary = parser.accepts("core-library", "Construct a core library");
+ numThreads = parser
+ .accepts("num-threads", "Number of threads to run with")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(1)
+ .describedAs(NUM_ARG);
+ incremental = parser.accepts("incremental", "Merge result with the output if it exists");
+ forceJumbo = parser.accepts("force-jumbo", "Force use of string-jumbo instructions");
+ noWarning = parser.accepts("no-warning", "Suppress warnings");
+ maxIndexNumber = parser.accepts("set-max-idx-number",
+ "Undocumented: Set maximal index number to use in a dex file.")
+ .withRequiredArg()
+ .ofType(Integer.class)
+ .defaultsTo(0)
+ .describedAs("Maximum index");
+ minimalMainDex = parser.accepts("minimal-main-dex", "Produce smallest possible main dex");
+ mainDexList = parser
+ .accepts("main-dex-list", "File listing classes that must be in the main dex file")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ multiDex =
+ parser
+ .accepts("multi-dex", "Allow generation of multi-dex")
+ .requiredIf(minimalMainDex, mainDexList, maxIndexNumber);
+ minApiLevel = parser
+ .accepts("min-sdk-version", "Minimum Android API level compatibility.")
+ .withRequiredArg().ofType(Integer.class);
+ inputList = parser
+ .accepts("input-list", "File listing input files")
+ .withRequiredArg()
+ .describedAs(FILE_ARG);
+ inputs = parser.nonOptions("Input files");
+ version = parser.accepts("version", "Print the version of this tool").forHelp();
+ help = parser.accepts("help", "Print this message").forHelp();
+ }
+ }
+
+ private DxCompatOptions(OptionSet options, Spec spec) {
+ help = options.has(spec.help);
+ version = options.has(spec.version);
+ debug = options.has(spec.debug);
+ verbose = options.has(spec.verbose);
+ if (options.has(spec.positions)) {
+ switch (options.valueOf(spec.positions)) {
+ case "none":
+ positions = PositionInfo.NONE;
+ break;
+ case "important":
+ positions = PositionInfo.IMPORTANT;
+ break;
+ case "lines":
+ positions = PositionInfo.LINES;
+ break;
+ case "throwing":
+ positions = PositionInfo.THROWING;
+ break;
+ default:
+ positions = PositionInfo.IMPORTANT;
+ break;
+ }
+ } else {
+ positions = PositionInfo.LINES;
+ }
+ noLocals = options.has(spec.noLocals);
+ noOptimize = options.has(spec.noOptimize);
+ statistics = options.has(spec.statistics);
+ optimizeList = options.valueOf(spec.optimizeList);
+ noOptimizeList = options.valueOf(spec.noOptimizeList);
+ noStrict = options.has(spec.noStrict);
+ keepClasses = options.has(spec.keepClasses);
+ output = options.valueOf(spec.output);
+ dumpTo = options.valueOf(spec.dumpTo);
+ dumpWidth = options.valueOf(spec.dumpWidth);
+ dumpMethod = options.valueOf(spec.dumpMethod);
+ dump = options.has(spec.dump);
+ verboseDump = options.has(spec.verboseDump);
+ noFiles = options.has(spec.noFiles);
+ coreLibrary = options.has(spec.coreLibrary);
+ numThreads = lastIntOf(options.valuesOf(spec.numThreads));
+ incremental = options.has(spec.incremental);
+ forceJumbo = options.has(spec.forceJumbo);
+ noWarning = options.has(spec.noWarning);
+ multiDex = options.has(spec.multiDex);
+ mainDexList = options.valueOf(spec.mainDexList);
+ minimalMainDex = options.has(spec.minimalMainDex);
+ if (options.has(spec.minApiLevel)) {
+ List<Integer> allMinApiLevels = options.valuesOf(spec.minApiLevel);
+ minApiLevel = allMinApiLevels.get(allMinApiLevels.size() - 1);
+ } else {
+ minApiLevel = AndroidApiLevel.getDefault().getLevel();
+ }
+ inputList = options.valueOf(spec.inputList);
+ inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs));
+ maxIndexNumber = options.valueOf(spec.maxIndexNumber);
+ }
+
+ public static DxCompatOptions parse(String[] args) {
+ Spec spec = new Spec();
+ return new DxCompatOptions(spec.parser.parse(args), spec);
+ }
+
+ private static int lastIntOf(List<Integer> values) {
+ assert !values.isEmpty();
+ return values.get(values.size() - 1);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ try {
+ run(args);
+ } catch (DxUsageMessage e) {
+ System.err.println(USAGE_HEADER);
+ e.printHelpOn(System.err);
+ System.exit(1);
+ } catch (CompilationFailedException e) {
+ System.exit(1);
+ }
+ }
+
+ private static void run(String[] args)
+ throws DxUsageMessage, IOException, CompilationFailedException {
+ DxCompatOptions dexArgs = DxCompatOptions.parse(args);
+ if (dexArgs.help) {
+ printHelpOn(System.out);
+ return;
+ }
+ if (dexArgs.version) {
+ System.out.println("CompatDx " + Version.getVersionString());
+ return;
+ }
+ CompilationMode mode = CompilationMode.RELEASE;
+ Path output = null;
+ List<Path> inputs = new ArrayList<>();
+ boolean singleDexFile = !dexArgs.multiDex;
+ Path mainDexList = null;
+ int numberOfThreads = 1;
+
+ for (String path : dexArgs.inputs) {
+ processPath(new File(path), inputs);
+ }
+ if (inputs.isEmpty()) {
+ if (dexArgs.noFiles) {
+ return;
+ }
+ throw new DxUsageMessage("No input files specified");
+ }
+
+ if (!Log.ENABLED && dexArgs.debug) {
+ System.out.println("Warning: logging is not enabled for this build.");
+ }
+
+ if (dexArgs.dump && dexArgs.verbose) {
+ System.out.println("Warning: dump is not supported");
+ }
+
+ if (dexArgs.verboseDump) {
+ throw new Unimplemented("verbose dump file not yet supported");
+ }
+
+ if (dexArgs.dumpMethod != null) {
+ throw new Unimplemented("method-dump not yet supported");
+ }
+
+ if (dexArgs.output != null) {
+ output = Paths.get(dexArgs.output);
+ if (FileUtils.isDexFile(output)) {
+ if (!singleDexFile) {
+ throw new DxUsageMessage("Cannot output to a single dex-file when running with multidex");
+ }
+ } else if (!FileUtils.isArchive(output)
+ && (!output.toFile().exists() || !output.toFile().isDirectory())) {
+ throw new DxUsageMessage("Unsupported output file or output directory does not exist. "
+ + "Output must be a directory or a file of type dex, apk, jar or zip.");
+ }
+ }
+
+ if (dexArgs.dumpTo != null && dexArgs.verbose) {
+ System.out.println("dump-to file not yet supported");
+ }
+
+ if (dexArgs.positions == PositionInfo.NONE && dexArgs.verbose) {
+ System.out.println("Warning: no support for positions none.");
+ }
+
+ if (dexArgs.positions == PositionInfo.LINES && !dexArgs.noLocals) {
+ mode = CompilationMode.DEBUG;
+ }
+
+ if (dexArgs.incremental) {
+ throw new Unimplemented("incremental merge not supported yet");
+ }
+
+ if (dexArgs.forceJumbo && dexArgs.verbose) {
+ System.out.println(
+ "Warning: no support for forcing jumbo-strings.\n"
+ + "Strings will only use jumbo-string indexing if necessary.\n"
+ + "Make sure that any dex merger subsequently used "
+ + "supports correct handling of jumbo-strings (eg, D8/R8 does).");
+ }
+
+ if (dexArgs.noOptimize && dexArgs.verbose) {
+ System.out.println("Warning: no support for not optimizing");
+ }
+
+ if (dexArgs.optimizeList != null) {
+ throw new Unimplemented("no support for optimize-method list");
+ }
+
+ if (dexArgs.noOptimizeList != null) {
+ throw new Unimplemented("no support for dont-optimize-method list");
+ }
+
+ if (dexArgs.statistics && dexArgs.verbose) {
+ System.out.println("Warning: no support for printing statistics");
+ }
+
+ if (dexArgs.numThreads > 1) {
+ numberOfThreads = dexArgs.numThreads;
+ }
+
+ if (dexArgs.mainDexList != null) {
+ mainDexList = Paths.get(dexArgs.mainDexList);
+ }
+
+ if (dexArgs.noStrict) {
+ if (dexArgs.verbose) {
+ System.out.println("Warning: conservative main-dex list not yet supported");
+ }
+ } else {
+ if (dexArgs.verbose) {
+ System.out.println("Warning: strict name checking not yet supported");
+ }
+ }
+
+ if (dexArgs.minimalMainDex && dexArgs.verbose) {
+ System.out.println("Warning: minimal main-dex support is not yet supported");
+ }
+
+ if (dexArgs.maxIndexNumber != 0 && dexArgs.verbose) {
+ System.out.println("Warning: internal maximum-index setting is not supported");
+ }
+
+ if (numberOfThreads < 1) {
+ throw new DxUsageMessage("Invalid numThreads value of " + numberOfThreads);
+ }
+ ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
+
+ try {
+ D8Command.Builder builder = D8Command.builder();
+ CompatDxHelper.ignoreDexInArchive(builder);
+ builder
+ .addProgramFiles(inputs)
+ .setProgramConsumer(
+ createConsumer(inputs, output, singleDexFile, dexArgs.keepClasses))
+ .setMode(mode)
+ .setMinApiLevel(dexArgs.minApiLevel);
+ if (mainDexList != null) {
+ builder.addMainDexListFiles(mainDexList);
+ }
+ CompatDxHelper.run(builder.build(), dexArgs.minimalMainDex);
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ private static ProgramConsumer createConsumer(
+ List<Path> inputs, Path output, boolean singleDexFile, boolean keepClasses)
+ throws DxUsageMessage {
+ if (output == null) {
+ return DexIndexedConsumer.emptyConsumer();
+ }
+ if (singleDexFile) {
+ return new SingleDexFileConsumer(
+ FileUtils.isDexFile(output)
+ ? new NamedDexFileConsumer(output)
+ : createDexConsumer(output, inputs, keepClasses));
+ }
+ return createDexConsumer(output, inputs, keepClasses);
+ }
+
+ private static DexIndexedConsumer createDexConsumer(
+ Path output, List<Path> inputs, boolean keepClasses)
+ throws DxUsageMessage {
+ if (keepClasses) {
+ if (!FileUtils.isArchive(output)) {
+ throw new DxCompatOptions.DxUsageMessage(
+ "Output must be an archive when --keep-classes is set.");
+ }
+ return new DexKeepClassesConsumer(output, inputs);
+ }
+ return FileUtils.isArchive(output)
+ ? new DexIndexedConsumer.ArchiveConsumer(output)
+ : new DexIndexedConsumer.DirectoryConsumer(output);
+ }
+
+ private static class SingleDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
+
+ private byte[] bytes = null;
+
+ public SingleDexFileConsumer(DexIndexedConsumer consumer) {
+ super(consumer);
+ }
+
+ @Override
+ public void accept(
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+ if (fileIndex > 0) {
+ throw new CompilationError(
+ "Compilation result could not fit into a single dex file. "
+ + "Reduce the input-program size or run with --multi-dex enabled");
+ }
+ assert bytes == null;
+ // Store a copy of the bytes as we may not assume the backing is valid after accept returns.
+ bytes = data.copyByteData();
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (bytes != null) {
+ super.accept(0, ByteDataView.of(bytes), null, handler);
+ }
+ super.finished(handler);
+ }
+ }
+
+ private static class NamedDexFileConsumer extends DexIndexedConsumer.ForwardingConsumer {
+ private final Path output;
+
+ public NamedDexFileConsumer(Path output) {
+ super(null);
+ this.output = output;
+ }
+
+ @Override
+ public void accept(
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+ StandardOpenOption[] options = {
+ StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
+ };
+ try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(output, options))) {
+ stream.write(data.getBuffer(), data.getOffset(), data.getLength());
+ } catch (IOException e) {
+ handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
+ }
+ }
+ }
+
+ private static class DexKeepClassesConsumer extends DexIndexedConsumer.ArchiveConsumer {
+
+ private final List<Path> inputs;
+
+ public DexKeepClassesConsumer(Path archive, List<Path> inputs) {
+ super(archive);
+ this.inputs = inputs;
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ try {
+ writeZipWithClasses(handler);
+ } catch (IOException e) {
+ handler.error(new ExceptionDiagnostic(e, getOrigin()));
+ }
+ super.finished(handler);
+ }
+
+ private void writeZipWithClasses(DiagnosticsHandler handler) throws IOException {
+ // For each input archive file, add all class files within.
+ for (Path input : inputs) {
+ if (FileUtils.isArchive(input)) {
+ try (ZipFile zipFile = FileUtils.createZipFile(input.toFile(), StandardCharsets.UTF_8)) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (ZipUtils.isClassFile(entry.getName())) {
+ try (InputStream entryStream = zipFile.getInputStream(entry)) {
+ byte[] bytes = ByteStreams.toByteArray(entryStream);
+ outputBuilder.addFile(entry.getName(), ByteDataView.of(bytes), handler);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static void printHelpOn(PrintStream sink) throws IOException {
+ sink.println(USAGE_HEADER);
+ new DxCompatOptions.Spec().parser.printHelpOn(sink);
+ }
+
+ private static void processPath(File file, List<Path> files) {
+ if (!file.exists()) {
+ throw new CompilationError("File does not exist: " + file);
+ }
+ if (file.isDirectory()) {
+ processDirectory(file, files);
+ return;
+ }
+ Path path = file.toPath();
+ if (FileUtils.isZipFile(path) || FileUtils.isJarFile(path) || FileUtils.isClassFile(path)) {
+ files.add(path);
+ return;
+ }
+ if (FileUtils.isApkFile(path)) {
+ throw new Unimplemented("apk files not yet supported");
+ }
+ }
+
+ private static void processDirectory(File directory, List<Path> files) {
+ assert directory.exists();
+ for (File file : directory.listFiles()) {
+ processPath(file, files);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 079b8bb..f573b76 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -7,6 +7,7 @@
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/** Provides immutable access to {@link FieldAccessInfoImpl}. */
public interface FieldAccessInfo {
@@ -29,5 +30,7 @@
boolean isWritten();
+ boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
+
boolean isWrittenOutside(DexEncodedMethod method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index a263cb6..5924435 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -147,6 +147,23 @@
}
/**
+ * Returns true if this field is written by a method for which {@param predicate} returns true.
+ */
+ @Override
+ public boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate) {
+ if (writesWithContexts != null) {
+ for (Set<DexEncodedMethod> encodedWriteContexts : writesWithContexts.values()) {
+ for (DexEncodedMethod encodedWriteContext : encodedWriteContexts) {
+ if (predicate.test(encodedWriteContext)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns true if this field is written by a method in the program other than {@param method}.
*/
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 4d8d00d..8feed9b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -98,7 +98,7 @@
}
private final Origin origin;
- private final JarApplicationReader application;
+ private JarApplicationReader application;
private CfCode code;
protected ReparseContext context;
private boolean reachabilitySensitive = false;
@@ -122,6 +122,8 @@
public CfCode asCfCode() {
if (code == null) {
ReparseContext context = this.context;
+ JarApplicationReader application = this.application;
+ assert application != null;
assert context != null;
// The ClassCodeVisitor is in charge of setting this.context to null.
try {
@@ -130,6 +132,7 @@
for (Code code : context.codeList) {
code.asLazyCfCode().code = null;
code.asLazyCfCode().context = context;
+ code.asLazyCfCode().application = application;
}
try {
parseCode(context, true);
@@ -155,6 +158,7 @@
assert this.context != null;
this.code = code;
this.context = null;
+ this.application = null;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 2c86c7b..17b91f3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -25,12 +25,12 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -56,9 +56,11 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
OptimizationFeedback feedback,
+ DexProgramClass clazz,
DexEncodedMethod method) {
+ assert clazz.type == method.method.holder;
this.appView = appView;
- this.clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ this.clazz = clazz;
this.code = code;
this.feedback = feedback;
this.method = method;
@@ -67,11 +69,29 @@
public static void run(
AppView<?> appView, IRCode code, OptimizationFeedback feedback, DexEncodedMethod method) {
- if (appView.enableWholeProgramOptimizations() && method.isClassInitializer()) {
- assert appView.appInfo().hasLiveness();
- new FieldValueAnalysis(appView.withLiveness(), code, feedback, method)
- .computeFieldOptimizationInfo();
+ if (!appView.enableWholeProgramOptimizations()) {
+ return;
}
+ assert appView.appInfo().hasLiveness();
+ if (!method.isInitializer()) {
+ return;
+ }
+ DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
+ if (method.isInstanceInitializer()) {
+ if (!appView.options().enableValuePropagationForInstanceFields) {
+ return;
+ }
+ DexEncodedMethod otherInstanceInitializer =
+ clazz.lookupDirectMethod(other -> other.isInstanceInitializer() && other != method);
+ if (otherInstanceInitializer != null) {
+ // Conservatively bail out.
+ // TODO(b/125282093): Handle multiple instance initializers on the same class.
+ return;
+ }
+ }
+
+ new FieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
+ .computeFieldOptimizationInfo();
}
private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
@@ -86,55 +106,51 @@
AppInfoWithLiveness appInfo = appView.appInfo();
DominatorTree dominatorTree = null;
- if (method.isClassInitializer()) {
- DexType context = method.method.holder;
+ DexType context = method.method.holder;
- // Find all the static-put instructions that assign a field in the enclosing class which is
- // guaranteed to be assigned only in the current initializer.
- boolean isStraightLineCode = true;
- Map<DexEncodedField, LinkedList<StaticPut>> staticPutsPerField = new IdentityHashMap<>();
- for (Instruction instruction : code.instructions()) {
- if (instruction.isStaticPut()) {
- StaticPut staticPut = instruction.asStaticPut();
- DexField field = staticPut.getField();
- DexEncodedField encodedField = appInfo.resolveField(field);
- if (encodedField != null
- && encodedField.field.holder == context
- && appInfo.isStaticFieldWrittenOnlyInEnclosingStaticInitializer(encodedField)) {
- staticPutsPerField
- .computeIfAbsent(encodedField, ignore -> new LinkedList<>())
- .add(staticPut);
- }
- }
- if (instruction.isJumpInstruction()) {
- if (!instruction.isGoto() && !instruction.isReturn()) {
- isStraightLineCode = false;
- }
+ // Find all the static-put instructions that assign a field in the enclosing class which is
+ // guaranteed to be assigned only in the current initializer.
+ boolean isStraightLineCode = true;
+ Map<DexEncodedField, LinkedList<FieldInstruction>> putsPerField = new IdentityHashMap<>();
+ for (Instruction instruction : code.instructions()) {
+ if (instruction.isFieldPut()) {
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
+ DexField field = fieldPut.getField();
+ DexEncodedField encodedField = appInfo.resolveField(field);
+ if (encodedField != null
+ && encodedField.field.holder == context
+ && appInfo.isFieldOnlyWrittenInMethod(encodedField, method)) {
+ putsPerField.computeIfAbsent(encodedField, ignore -> new LinkedList<>()).add(fieldPut);
}
}
-
- List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- for (Entry<DexEncodedField, LinkedList<StaticPut>> entry : staticPutsPerField.entrySet()) {
- DexEncodedField encodedField = entry.getKey();
- LinkedList<StaticPut> staticPuts = entry.getValue();
- if (staticPuts.size() > 1) {
- continue;
+ if (instruction.isJumpInstruction()) {
+ if (!instruction.isGoto() && !instruction.isReturn()) {
+ isStraightLineCode = false;
}
- StaticPut staticPut = staticPuts.getFirst();
- if (!isStraightLineCode) {
- if (dominatorTree == null) {
- dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
- }
- if (!dominatorTree.dominatesAllOf(staticPut.getBlock(), normalExitBlocks)) {
- continue;
- }
- }
- if (fieldMaybeReadBeforeInstruction(encodedField, staticPut)) {
- continue;
- }
- updateFieldOptimizationInfo(encodedField, staticPut.value());
}
}
+
+ List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
+ for (Entry<DexEncodedField, LinkedList<FieldInstruction>> entry : putsPerField.entrySet()) {
+ DexEncodedField encodedField = entry.getKey();
+ LinkedList<FieldInstruction> fieldPuts = entry.getValue();
+ if (fieldPuts.size() > 1) {
+ continue;
+ }
+ FieldInstruction fieldPut = fieldPuts.getFirst();
+ if (!isStraightLineCode) {
+ if (dominatorTree == null) {
+ dominatorTree = new DominatorTree(code, Assumption.NO_UNREACHABLE_BLOCKS);
+ }
+ if (!dominatorTree.dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+ continue;
+ }
+ }
+ if (fieldMaybeReadBeforeInstruction(encodedField, fieldPut)) {
+ continue;
+ }
+ updateFieldOptimizationInfo(encodedField, fieldPut.value());
+ }
}
private boolean fieldMaybeReadBeforeInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index fce7b09..92954e4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
// TODO(b/112437944): Handle cycles in the graph + add a test that fails with the current
// implementation. The current caching mechanism is unsafe, because we may mark a message as not
@@ -222,6 +223,19 @@
}
if (newlyLiveField != null) {
+ // Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in
+ // the app. This is needed to ensure that field writes are not removed from the app.
+ DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
+ assert defaultInitializer != null;
+ Predicate<DexEncodedMethod> neitherDefaultConstructorNorDynamicMethod =
+ writer -> writer != defaultInitializer && writer != dynamicMethod;
+ if (enqueuer.isFieldWrittenInMethodSatisfying(
+ newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
+ enqueuer.registerFieldRead(newlyLiveField.field, dynamicMethod);
+ }
+
+ // Unconditionally register the hazzer and one-of proto fields as written from
+ // dynamicMethod().
if (enqueuer.registerFieldWrite(newlyLiveField.field, dynamicMethod)) {
worklist.enqueueMarkReachableFieldAction(
clazz, newlyLiveField, KeepReason.reflectiveUseIn(dynamicMethod));
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 55cabed..682804e 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
@@ -136,7 +136,7 @@
LoadStoreHelper loadStoreHelper = new LoadStoreHelper(appView, code, typeVerificationHelper);
loadStoreHelper.insertLoadsAndStores();
// Run optimizations on phis and basic blocks in a fixpoint.
- if (!appView.options().testing.disallowLoadStoreOptimization) {
+ if (appView.options().enableLoadStoreOptimization) {
PhiOptimizations phiOptimizations = new PhiOptimizations();
boolean reachedFixpoint = false;
phiOptimizations.optimize(code);
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 3e9ecab..45be243 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
@@ -943,10 +943,18 @@
private DexType computeOutlineClassType() {
DexType result;
int count = 0;
+ String tempPrefix =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getSynthesizedLibraryClassesPackagePrefix(appView);
+ String prefix = tempPrefix.replace('/', '.');
do {
- String name = OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
+ String name =
+ prefix + OutlineOptions.CLASS_NAME + (count == 0 ? "" : Integer.toString(count));
count++;
result = appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(name));
+
} while (appView.definitionFor(result) != null);
return result;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 511b971..41995ce 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -59,7 +59,6 @@
public final class BackportedMethodRewriter {
public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
- private static final String UTILITY_CLASS_DESCRIPTOR_PREFIX = "L" + UTILITY_CLASS_NAME_PREFIX;
private final AppView<?> appView;
private final IRConverter converter;
@@ -147,7 +146,7 @@
// On R8 resolution is immediate, on d8 it may look-up.
DexClass current = appView.definitionFor(method.holder);
if (current == null) {
- return null;
+ return null;
}
DexEncodedMethod dexEncodedMethod = current.lookupVirtualMethod(method);
if (dexEncodedMethod != null) {
@@ -177,7 +176,7 @@
}
public static boolean hasRewrittenMethodPrefix(DexType clazz) {
- return clazz.descriptor.toString().startsWith(UTILITY_CLASS_DESCRIPTOR_PREFIX);
+ return clazz.descriptor.toString().contains(UTILITY_CLASS_NAME_PREFIX);
}
public void synthesizeUtilityClasses(Builder<?> builder, ExecutorService executorService)
@@ -1425,15 +1424,19 @@
DexItemFactory factory = appView.dexItemFactory();
String unqualifiedName = method.holder.getName();
// Avoid duplicate class names between core lib dex file and program dex files.
- String desugaredLibUtilitySuffix =
- appView.options().isDesugaredLibraryCompilation() ? "$desugaredLib" : "";
+ String desugaredLibPrefix =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getSynthesizedLibraryClassesPackagePrefix(appView);
String descriptor =
- UTILITY_CLASS_DESCRIPTOR_PREFIX
+ "L"
+ + desugaredLibPrefix
+ + UTILITY_CLASS_NAME_PREFIX
+ '$'
+ unqualifiedName
+ '$'
+ method.proto.parameters.size()
- + desugaredLibUtilitySuffix
+ '$'
+ methodName
+ ';';
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index dda019e..1c11d78 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -23,9 +24,12 @@
public class DesugaredLibraryConfiguration {
+ public static final String FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX = "j$/";
+
// TODO(b/134732760): should use DexString, DexType, DexMethod or so on when possible.
private final AndroidApiLevel requiredCompilationAPILevel;
private final boolean libraryCompilation;
+ private final String synthesizedLibraryClassesPackagePrefix;
private final Map<String, String> rewritePrefix;
private final Map<DexType, DexType> emulateLibraryInterface;
private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
@@ -43,6 +47,7 @@
return new DesugaredLibraryConfiguration(
AndroidApiLevel.B,
false,
+ FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
prefix,
ImmutableMap.of(),
ImmutableMap.of(),
@@ -56,6 +61,7 @@
return new DesugaredLibraryConfiguration(
AndroidApiLevel.B,
false,
+ FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
@@ -68,6 +74,7 @@
public DesugaredLibraryConfiguration(
AndroidApiLevel requiredCompilationAPILevel,
boolean libraryCompilation,
+ String packagePrefix,
Map<String, String> rewritePrefix,
Map<DexType, DexType> emulateLibraryInterface,
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
@@ -77,6 +84,7 @@
List<String> extraKeepRules) {
this.requiredCompilationAPILevel = requiredCompilationAPILevel;
this.libraryCompilation = libraryCompilation;
+ this.synthesizedLibraryClassesPackagePrefix = packagePrefix;
this.rewritePrefix = rewritePrefix;
this.emulateLibraryInterface = emulateLibraryInterface;
this.retargetCoreLibMember = retargetCoreLibMember;
@@ -100,6 +108,12 @@
return libraryCompilation;
}
+ public String getSynthesizedLibraryClassesPackagePrefix(AppView<?> appView) {
+ return appView.options().isDesugaredLibraryCompilation()
+ ? synthesizedLibraryClassesPackagePrefix
+ : "";
+ }
+
public Map<String, String> getRewritePrefix() {
return rewritePrefix;
}
@@ -134,6 +148,8 @@
private AndroidApiLevel requiredCompilationAPILevel;
private boolean libraryCompilation = false;
+ private String synthesizedLibraryClassesPackagePrefix =
+ FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX;
private Map<String, String> rewritePrefix = new HashMap<>();
private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
@@ -146,6 +162,12 @@
this.factory = dexItemFactory;
}
+ public Builder setSynthesizedLibraryClassesPackagePrefix(String prefix) {
+ String replace = prefix.replace('.', '/');
+ this.synthesizedLibraryClassesPackagePrefix = replace;
+ return this;
+ }
+
public Builder setRequiredCompilationAPILevel(AndroidApiLevel requiredCompilationAPILevel) {
this.requiredCompilationAPILevel = requiredCompilationAPILevel;
return this;
@@ -231,6 +253,7 @@
return new DesugaredLibraryConfiguration(
requiredCompilationAPILevel,
libraryCompilation,
+ synthesizedLibraryClassesPackagePrefix,
ImmutableMap.copyOf(rewritePrefix),
ImmutableMap.copyOf(emulateLibraryInterface),
ImmutableMap.copyOf(retargetCoreLibMember),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index 4de1fe1..b66432e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -19,7 +19,7 @@
public class DesugaredLibraryConfigurationParser {
- private static final int MAX_SUPPORTED_VERSION = 2;
+ private static final int MAX_SUPPORTED_VERSION = 3;
private final DesugaredLibraryConfiguration.Builder configurationBuilder;
private final Reporter reporter;
@@ -67,6 +67,17 @@
+ "distributed only in the early canary versions. Please update for "
+ "production releases and to fix this warning."));
}
+
+ if (jsonConfig.has("synthesized_library_classes_package_prefix")) {
+ configurationBuilder.setSynthesizedLibraryClassesPackagePrefix(
+ jsonConfig.get("synthesized_library_classes_package_prefix").getAsString());
+ } else {
+ reporter.warning(
+ new StringDiagnostic(
+ "Missing package_prefix, falling back to "
+ + DesugaredLibraryConfiguration.FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX
+ + " prefix, update desugared library configuration."));
+ }
int required_compilation_api_level =
jsonConfig.get("required_compilation_api_level").getAsInt();
configurationBuilder.setRequiredCompilationAPILevel(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 5d7c31f..d75b84c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -32,7 +32,6 @@
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
@@ -95,7 +94,6 @@
public class DesugaredLibraryWrapperSynthesizer {
public static final String WRAPPER_PREFIX = "$r8$wrapper$";
- public static final String WRAPPER_DESCRIPTOR_PREFIX = "L" + WRAPPER_PREFIX;
public static final String TYPE_WRAPPER_SUFFIX = "$-WRP";
public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
@@ -120,7 +118,7 @@
}
public static boolean isSynthesizedWrapper(DexType clazz) {
- return clazz.descriptor.toString().startsWith(WRAPPER_DESCRIPTOR_PREFIX);
+ return clazz.descriptor.toString().contains(WRAPPER_PREFIX);
}
boolean hasSynthesized(DexType type) {
@@ -152,10 +150,18 @@
}
private DexType createWrapperType(DexType type, String suffix) {
- String prefix = appView.options().isDesugaredLibraryCompilation() ? "lib$" : "";
+ String desugaredLibPrefix =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getSynthesizedLibraryClassesPackagePrefix(appView);
return factory.createType(
- DescriptorUtils.javaTypeToDescriptor(
- WRAPPER_PREFIX + prefix + type.toString().replace('.', '$') + suffix));
+ "L"
+ + desugaredLibPrefix
+ + WRAPPER_PREFIX
+ + type.toString().replace('.', '$')
+ + suffix
+ + ";");
}
private DexType getWrapper(
@@ -260,7 +266,8 @@
Collections.emptyList());
}
- private ChecksumSupplier getChecksumSupplier(DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
+ private ChecksumSupplier getChecksumSupplier(
+ DesugaredLibraryWrapperSynthesizer synthesizer, DexType keyType) {
return clazz -> {
// The synthesized type wrappers are constructed lazily, so their lookup must be delayed
// until the point the checksum is requested (at write time). The presence of a wrapper
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 6a9c3af..d26e953 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -474,6 +474,9 @@
// Then, PACKAGE is more restrictive constraint.
return one;
}
+ if (appView.isSubtype(one.targetHolder, other.targetHolder).isTrue()) {
+ return new ConstraintWithTarget(Constraint.SAMECLASS, one.targetHolder);
+ }
// TODO(b/128967328): towards finer-grained constraints, we need both.
// The target method is still inlineable to methods with a holder from the same package of
// one's holder and a subtype of other's holder.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 98f5a47..436f81b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -722,7 +722,7 @@
if (method.isInstanceInitializer()) {
InstanceInitializerInfo initializerInfo =
method.getOptimizationInfo().getInstanceInitializerInfo();
- if (!initializerInfo.isEligibleForClassInlining()) {
+ if (initializerInfo.receiverMayEscapeOutsideConstructorChain()) {
return null;
}
}
@@ -838,7 +838,8 @@
}
private InliningInfo isEligibleIndirectVirtualMethodCall(DexMethod callee) {
- DexEncodedMethod singleTarget = eligibleClassDefinition.lookupVirtualMethod(callee);
+ DexEncodedMethod singleTarget =
+ appView.appInfo().resolveMethod(eligibleClassDefinition, callee).getSingleTarget();
if (isEligibleSingleTarget(singleTarget)) {
return isEligibleVirtualMethodCall(
null, callee, singleTarget, eligibility -> eligibility.returnsReceiver.isFalse());
@@ -1077,7 +1078,7 @@
}
InstanceInitializerInfo initializerInfo =
encodedMethod.getOptimizationInfo().getInstanceInitializerInfo();
- return initializerInfo.isEligibleForClassInlining();
+ return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
}
private boolean exemptFromInstructionLimit(DexEncodedMethod inlinee) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index ee49c97..eac8791 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -5,6 +5,16 @@
import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
+import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.DEX_ITEM_BASED_CONST_STRING;
+import static com.android.tools.r8.ir.code.Opcodes.GOTO;
+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.RETURN;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -392,79 +402,78 @@
// Defining a finalize method can observe the side-effect of Object.<init> GC registration.
return null;
}
+ NonTrivialInstanceInitializerInfo.Builder builder = NonTrivialInstanceInitializerInfo.builder();
Value receiver = code.getThis();
- for (Instruction insn : code.instructions()) {
- if (insn.isReturn()) {
- continue;
- }
+ for (Instruction instruction : code.instructions()) {
+ switch (instruction.opcode()) {
+ case ARGUMENT:
+ case ASSUME:
+ case CONST_NUMBER:
+ case RETURN:
+ break;
- if (insn.isAssume()) {
- continue;
- }
-
- if (insn.isArgument()) {
- continue;
- }
-
- if (insn.isConstInstruction()) {
- if (insn.instructionInstanceCanThrow()) {
- return null;
- } else {
- continue;
- }
- }
-
- if (insn.isInvokeDirect()) {
- InvokeDirect invokedDirect = insn.asInvokeDirect();
- DexMethod invokedMethod = invokedDirect.getInvokedMethod();
- if (!dexItemFactory.isConstructor(invokedMethod)) {
- return null;
- }
- if (invokedMethod.holder != clazz.superType) {
- return null;
- }
- // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
- if (invokedMethod == dexItemFactory.enumMethods.constructor
- || invokedMethod == dexItemFactory.objectMethods.constructor) {
- continue;
- }
- DexEncodedMethod callTarget = appView.definitionFor(invokedMethod);
- if (callTarget == null
- || callTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
- || invokedDirect.getReceiver() != receiver) {
- return null;
- }
- for (Value value : invokedDirect.inValues()) {
- if (value != receiver && !(value.isConstant() || value.isArgument())) {
+ case CONST_CLASS:
+ case CONST_STRING:
+ case DEX_ITEM_BASED_CONST_STRING:
+ if (instruction.instructionMayTriggerMethodInvocation(appView, clazz.type)) {
return null;
}
- }
- continue;
- }
+ break;
- if (insn.isInstancePut()) {
- InstancePut instancePut = insn.asInstancePut();
- DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
- if (field == null
- || instancePut.object() != receiver
- || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+ case GOTO:
+ // Trivial goto to the next block.
+ if (!instruction.asGoto().isTrivialGotoToTheNextBlock(code)) {
+ return null;
+ }
+ break;
+
+ case INSTANCE_PUT:
+ {
+ InstancePut instancePut = instruction.asInstancePut();
+ DexEncodedField field = appView.appInfo().resolveFieldOn(clazz, instancePut.getField());
+ if (field == null
+ || instancePut.object() != receiver
+ || (instancePut.value() != receiver && !instancePut.value().isArgument())) {
+ return null;
+ }
+ }
+ break;
+
+ case INVOKE_DIRECT:
+ {
+ InvokeDirect invoke = instruction.asInvokeDirect();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (!dexItemFactory.isConstructor(invokedMethod)) {
+ return null;
+ }
+ if (invokedMethod.holder != clazz.superType) {
+ return null;
+ }
+ // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
+ if (invokedMethod == dexItemFactory.enumMethods.constructor
+ || invokedMethod == dexItemFactory.objectMethods.constructor) {
+ break;
+ }
+ DexEncodedMethod singleTarget = appView.definitionFor(invokedMethod);
+ if (singleTarget == null
+ || singleTarget.getOptimizationInfo().getInstanceInitializerInfo().isDefaultInfo()
+ || invoke.getReceiver() != receiver) {
+ return null;
+ }
+ for (Value value : invoke.inValues()) {
+ if (value != receiver && !(value.isConstant() || value.isArgument())) {
+ return null;
+ }
+ }
+ }
+ break;
+
+ default:
+ // Other instructions make the instance initializer not eligible.
return null;
- }
- continue;
}
-
- if (insn.isGoto()) {
- // Trivial goto to the next block.
- if (insn.asGoto().isTrivialGotoToTheNextBlock(code)) {
- continue;
- }
- return null;
- }
-
- // Other instructions make the instance initializer not eligible.
- return null;
}
- return NonTrivialInstanceInitializerInfo.INSTANCE;
+ return builder.build();
}
private void identifyInvokeSemanticsForInlining(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index a75a255..e93cda7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -29,16 +29,6 @@
}
@Override
- public boolean isEligibleForClassInlining() {
- return false;
- }
-
- @Override
- public boolean isEligibleForClassStaticizing() {
- return false;
- }
-
- @Override
public boolean instanceFieldInitializationMayDependOnEnvironment() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index 2614eb3..24cb9af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -14,10 +14,6 @@
*/
public abstract AbstractFieldSet readSet();
- public abstract boolean isEligibleForClassInlining();
-
- public abstract boolean isEligibleForClassStaticizing();
-
/**
* Returns true if one of the instance fields on the enclosing class may be initialized with a
* value that may depend on the runtime environment by this constructor.
@@ -47,6 +43,10 @@
*/
public abstract boolean receiverNeverEscapesOutsideConstructorChain();
+ public final boolean receiverMayEscapeOutsideConstructorChain() {
+ return !receiverNeverEscapesOutsideConstructorChain();
+ }
+
public boolean isDefaultInfo() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 67ad395..7802e12 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -4,43 +4,108 @@
package com.android.tools.r8.ir.optimize.info.initializer;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
public final class NonTrivialInstanceInitializerInfo extends InstanceInitializerInfo {
- public static final NonTrivialInstanceInitializerInfo INSTANCE =
- new NonTrivialInstanceInitializerInfo();
+ private static final int INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT = 1 << 0;
+ private static final int NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS = 1 << 1;
+ private static final int RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN = 1 << 2;
- private NonTrivialInstanceInitializerInfo() {}
+ private final int data;
+ private final AbstractFieldSet readSet;
+
+ private NonTrivialInstanceInitializerInfo(int data, AbstractFieldSet readSet) {
+ assert verifyNoUnknownBits(data);
+ this.data = data;
+ this.readSet = readSet;
+ }
+
+ private static boolean verifyNoUnknownBits(int data) {
+ int knownBits =
+ INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+ | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+ | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ assert (data & ~knownBits) == 0;
+ return true;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
@Override
public AbstractFieldSet readSet() {
- return EmptyFieldSet.getInstance();
- }
-
- @Override
- public boolean isEligibleForClassInlining() {
- return true;
- }
-
- @Override
- public boolean isEligibleForClassStaticizing() {
- return true;
+ return readSet;
}
@Override
public boolean instanceFieldInitializationMayDependOnEnvironment() {
- return false;
+ return (data & INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT) == 0;
}
@Override
public boolean mayHaveOtherSideEffectsThanInstanceFieldAssignments() {
- return false;
+ return (data & NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS) == 0;
}
@Override
public boolean receiverNeverEscapesOutsideConstructorChain() {
- return true;
+ return (data & RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN) != 0;
+ }
+
+ public static class Builder {
+
+ private int data =
+ INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
+ | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
+ | RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ private AbstractFieldSet readSet = EmptyFieldSet.getInstance();
+
+ private boolean isTrivial() {
+ return data == 0 && readSet.isTop();
+ }
+
+ public Builder markFieldAsRead(DexEncodedField field) {
+ if (readSet.isKnownFieldSet()) {
+ if (readSet.isBottom()) {
+ readSet = new ConcreteMutableFieldSet(field);
+ } else {
+ readSet.asConcreteFieldSet().add(field);
+ }
+ }
+ assert readSet.contains(field);
+ return this;
+ }
+
+ public Builder markAllFieldsAsRead() {
+ readSet = UnknownFieldSet.getInstance();
+ return this;
+ }
+
+ public Builder setInstanceFieldInitializationMayDependOnEnvironment() {
+ data &= ~INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT;
+ return this;
+ }
+
+ public Builder setMayHaveOtherSideEffectsThanInstanceFieldAssignments() {
+ data &= ~NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS;
+ return this;
+ }
+
+ public Builder setReceiverMayEscapeOutsideConstructorChain() {
+ data &= ~RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN;
+ return this;
+ }
+
+ public InstanceInitializerInfo build() {
+ return isTrivial()
+ ? DefaultInstanceInitializerInfo.getInstance()
+ : new NonTrivialInstanceInitializerInfo(data, readSet);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 12992ca..e019acd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -27,7 +27,6 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer.CandidateInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -155,9 +154,7 @@
// fields this should guarantee that the constructor is empty.
assert candidateClass.instanceFields().size() == 0;
assert constructorUsed.isProcessed();
- InstanceInitializerInfo initializerInfo =
- constructorUsed.getOptimizationInfo().getInstanceInitializerInfo();
- if (!initializerInfo.isEligibleForClassStaticizing()) {
+ if (constructorUsed.getOptimizationInfo().mayHaveSideEffects()) {
it.remove();
continue;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
new file mode 100644
index 0000000..fcf1453
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -0,0 +1,332 @@
+// 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.kotlin;
+
+import com.android.tools.r8.naming.Range;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+// This is a parser for the Kotlin-generated source debug extensions, which is a stratified map.
+// The kotlin-parser for this data is can be found here in the kotlin compiler:
+// compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt
+public class KotlinSourceDebugExtensionParser {
+
+ private static final String SMAP_IDENTIFIER = "SMAP";
+ private static final String SMAP_SECTION_START = "*S";
+ private static final String SMAP_SECTION_KOTLIN_START = SMAP_SECTION_START + " Kotlin";
+ private static final String SMAP_FILES_IDENTIFIER = "*F";
+ private static final String SMAP_LINES_IDENTIFIER = "*L";
+ private static final String SMAP_END_IDENTIFIER = "*E";
+
+ public static class KotlinSourceDebugExtensionParserException extends Exception {
+
+ KotlinSourceDebugExtensionParserException(String message) {
+ super(message);
+ }
+ }
+
+ public static class BufferedStringReader implements Closeable {
+
+ private final BufferedReader reader;
+
+ private String readLine;
+
+ BufferedStringReader(String data) {
+ reader = new BufferedReader(new StringReader(data));
+ }
+
+ String readNextLine() throws IOException {
+ return readLine = reader.readLine();
+ }
+
+ boolean readExpectedLine(String expected) throws IOException {
+ return readNextLine().equals(expected);
+ }
+
+ void readExpectedLineOrThrow(String expected)
+ throws KotlinSourceDebugExtensionParserException, IOException {
+ if (!readExpectedLine(expected)) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "The string " + readLine + " does not match the expected string " + expected);
+ }
+ }
+
+ boolean isEOF() {
+ return readLine == null;
+ }
+
+ BufferedStringReader readUntil(String terminator) throws IOException {
+ while (!terminator.equals(readLine) && !isEOF()) {
+ readNextLine();
+ }
+ if (isEOF()) {
+ return this;
+ }
+ return this;
+ }
+
+ void readUntil(
+ String terminator,
+ int linesInBlock,
+ ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
+ throws IOException, KotlinSourceDebugExtensionParserException {
+ if (terminator.equals(readLine)) {
+ return;
+ }
+ List<String> readStrings = new ArrayList<>();
+ readStrings.add(readNextLine());
+ int linesLeft = linesInBlock;
+ while (!terminator.equals(readLine) && !isEOF()) {
+ if (linesLeft == 1) {
+ assert readStrings.size() == linesInBlock;
+ callback.accept(readStrings);
+ linesLeft = linesInBlock;
+ readStrings = new ArrayList<>();
+ } else {
+ linesLeft -= 1;
+ }
+ readStrings.add(readNextLine());
+ }
+ if (readStrings.size() > 0 && !readStrings.get(0).equals(terminator)) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Block size does not match linesInBlock = " + linesInBlock);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+ }
+
+ public static Result parse(String annotationData) {
+ if (annotationData == null || annotationData.isEmpty()) {
+ return null;
+ }
+
+ // The source debug smap data is on the following form:
+ // SMAP
+ // <filename>
+ // Kotlin <-- why is this even here?
+ // *S <section>
+ // *F
+ // + <file_id_i> <file_name_i>
+ // <path_i>
+ // *L
+ // <from>#<file>,<to>:<debug-line-position>
+ // <from>#<file>:<debug-line-position>
+ // *E
+ // *S KotlinDebug
+ // ***
+ // *E
+ try (BufferedStringReader reader = new BufferedStringReader(annotationData)) {
+ ResultBuilder builder = new ResultBuilder();
+ // Check for SMAP
+ if (!reader.readExpectedLine(SMAP_IDENTIFIER)) {
+ return null;
+ }
+ if (reader.readUntil(SMAP_SECTION_KOTLIN_START).isEOF()) {
+ return null;
+ }
+ // At this point we should be parsing a kotlin source debug extension, so we will throw if we
+ // read an unexpected line.
+ reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
+ // Iterate over the files section with the format:
+ // + <file_number_i> <file_name_i>
+ // <file_path_i>
+ reader.readUntil(
+ SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
+
+ // Ensure that we read *L.
+ if (reader.isEOF()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Unexpected EOF - no debug line positions");
+ }
+ // Iterate over the debug line number positions:
+ // <from>#<file>,<to>:<debug-line-position>
+ // or
+ // <from>#<file>:<debug-line-position>
+ reader.readUntil(
+ SMAP_END_IDENTIFIER, 1, block -> addDebugEntryToBuilder(block.get(0), builder));
+
+ // Ensure that we read the end section *E.
+ if (reader.isEOF()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Unexpected EOF when parsing SMAP debug entries");
+ }
+
+ return builder.build();
+ } catch (IOException | KotlinSourceDebugExtensionParserException e) {
+ return null;
+ }
+ }
+
+ private static void addFileToBuilder(String entryLine, String filePath, ResultBuilder builder)
+ throws KotlinSourceDebugExtensionParserException {
+ // + <file_number_i> <file_name_i>
+ // <file_path_i>
+ String[] entries = entryLine.trim().split(" ");
+ if (entries.length != 3 || !entries[0].equals("+")) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Wrong number of entries on line " + entryLine);
+ }
+ String fileName = entries[2];
+ if (fileName.isEmpty()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Did not expect file name to be empty for line " + entryLine);
+ }
+ if (filePath == null || filePath.isEmpty()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Did not expect file path to be null or empty for " + entryLine);
+ }
+ int index = asInteger(entries[1]);
+ Source source = new Source(fileName, filePath);
+ Source existingSource = builder.files.put(index, source);
+ if (existingSource != null) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "File index " + index + " was already mapped to an existing source: " + source);
+ }
+ }
+
+ private static int asInteger(String numberAsString)
+ throws KotlinSourceDebugExtensionParserException {
+ int number = -1;
+ try {
+ number = Integer.parseInt(numberAsString);
+ } catch (NumberFormatException e) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Could not parse number " + numberAsString);
+ }
+ return number;
+ }
+
+ private static void addDebugEntryToBuilder(String debugEntry, ResultBuilder builder)
+ throws KotlinSourceDebugExtensionParserException {
+ // <from>#<file>,<to>:<debug-line-position>
+ // or
+ // <from>#<file>:<debug-line-position>
+ try {
+ int targetSplit = debugEntry.indexOf(':');
+ int target = Integer.parseInt(debugEntry.substring(targetSplit + 1));
+ String original = debugEntry.substring(0, targetSplit);
+ int fileIndexSplit = original.indexOf('#');
+ int originalStart = Integer.parseInt(original.substring(0, fileIndexSplit));
+ // The range may have a different end than start.
+ String fileAndEndRange = original.substring(fileIndexSplit + 1);
+ int endRangeCharPosition = fileAndEndRange.indexOf(',');
+ int originalEnd = originalStart;
+ if (endRangeCharPosition > -1) {
+ // The file should be at least one number wide.
+ assert endRangeCharPosition > 0;
+ originalEnd = Integer.parseInt(fileAndEndRange.substring(endRangeCharPosition + 1));
+ } else {
+ endRangeCharPosition = fileAndEndRange.length();
+ }
+ int fileIndex = Integer.parseInt(fileAndEndRange.substring(0, endRangeCharPosition));
+ Source thisFileSource = builder.files.get(fileIndex);
+ if (thisFileSource != null) {
+ Range range = new Range(originalStart, originalEnd);
+ Position position = new Position(thisFileSource, range);
+ Position existingPosition = builder.positions.put(target, position);
+ assert existingPosition == null
+ : "Position index "
+ + target
+ + " was already mapped to an existing position: "
+ + position;
+ }
+ } catch (NumberFormatException e) {
+ throw new KotlinSourceDebugExtensionParserException("Could not convert position to number");
+ }
+ }
+
+ public static class Result {
+
+ private final Map<Integer, Source> files;
+ private final Map<Integer, Position> positions;
+
+ private Result(Map<Integer, Source> files, Map<Integer, Position> positions) {
+ this.files = files;
+ this.positions = positions;
+ }
+
+ public Map<Integer, Source> getFiles() {
+ return files;
+ }
+
+ public Map<Integer, Position> getPositions() {
+ return positions;
+ }
+ }
+
+ public static class ResultBuilder {
+ final Map<Integer, Source> files = new HashMap<>();
+ final Map<Integer, Position> positions = new HashMap<>();
+
+ public Result build() {
+ return new Result(files, positions);
+ }
+ }
+
+ public static class Source {
+ private final String fileName;
+ private final String path;
+
+ private Source(String fileName, String path) {
+ this.fileName = fileName;
+ this.path = path;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public String toString() {
+ return path + "(" + fileName + ")";
+ }
+ }
+
+ public static class Position {
+ private final Source source;
+ private final Range range;
+
+ public Position(Source source, Range range) {
+ this.source = source;
+ this.range = range;
+ }
+
+ public Source getSource() {
+ return source;
+ }
+
+ public Range getRange() {
+ return range;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(range.from);
+ sb.append("#");
+ sb.append(source);
+ if (range.to != range.from) {
+ sb.append(",");
+ sb.append(range.to);
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
index c5284ea..2874fc8 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBase.java
@@ -7,6 +7,7 @@
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;
public interface RetraceBase {
@@ -16,6 +17,8 @@
RetraceClassResult retrace(ClassReference classReference);
+ RetraceTypeResult retrace(TypeReference typeReference);
+
String retraceSourceFile(ClassReference classReference, String sourceFile);
String retraceSourceFile(
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
index ff196c1..c60cd91 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceBaseImpl.java
@@ -8,6 +8,7 @@
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 com.android.tools.r8.utils.Box;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
@@ -49,6 +50,11 @@
}
@Override
+ public RetraceTypeResult retrace(TypeReference typeReference) {
+ return new RetraceTypeResult(typeReference, this);
+ }
+
+ @Override
public String retraceSourceFile(
ClassReference obfuscatedClass,
String sourceFile,
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
new file mode 100644
index 0000000..b04d9a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -0,0 +1,55 @@
+// 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.retrace;
+
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceTypeResult.Element;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+public class RetraceTypeResult extends Result<Element, RetraceTypeResult> {
+
+ private final TypeReference obfuscatedType;
+ private final RetraceBase retraceBase;
+
+ RetraceTypeResult(TypeReference obfuscatedType, RetraceBase retraceBase) {
+ this.obfuscatedType = obfuscatedType;
+ this.retraceBase = retraceBase;
+ }
+
+ public Stream<Element> stream() {
+ // Handle void and primitive types as single element results.
+ if (obfuscatedType == null || obfuscatedType.isPrimitive()) {
+ return Stream.of(new Element(obfuscatedType));
+ }
+ if (obfuscatedType.isArray()) {
+ int dimensions = obfuscatedType.asArray().getDimensions();
+ return retraceBase.retrace(obfuscatedType.asArray().getBaseType()).stream()
+ .map(base -> new Element(Reference.array(base.getTypeReference(), dimensions)));
+ }
+ return retraceBase.retrace(obfuscatedType.asClass()).stream()
+ .map(clazz -> new Element(clazz.getClassReference()));
+ }
+
+ @Override
+ public RetraceTypeResult forEach(Consumer<Element> resultConsumer) {
+ stream().forEach(resultConsumer);
+ return this;
+ }
+
+ public static class Element {
+
+ private final TypeReference typeReference;
+
+ public Element(TypeReference typeReference) {
+ this.typeReference = typeReference;
+ }
+
+ public TypeReference getTypeReference() {
+ return typeReference;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index d1028f2..69e1930 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -761,22 +761,26 @@
return false;
}
- public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+ public boolean isFieldOnlyWrittenInMethod(DexEncodedField field, DexEncodedMethod method) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
if (!isPinned(field.field)) {
- DexEncodedMethod staticInitializer =
- definitionFor(field.field.holder).asProgramClass().getClassInitializer();
- if (staticInitializer != null) {
- FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
- return fieldAccessInfo != null
- && fieldAccessInfo.isWritten()
- && !fieldAccessInfo.isWrittenOutside(staticInitializer);
- }
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
+ return fieldAccessInfo != null
+ && fieldAccessInfo.isWritten()
+ && !fieldAccessInfo.isWrittenOutside(method);
}
return false;
}
+ public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+ assert checkIfObsolete();
+ assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
+ DexEncodedMethod staticInitializer =
+ definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+ return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
+ }
+
public boolean mayPropagateValueFor(DexReference reference) {
assert checkIfObsolete();
return !isPinned(reference) && !neverPropagateValue.contains(reference);
@@ -1022,7 +1026,9 @@
DexProgramClass refinedHolder =
(refinedReceiverIsStrictSubType ? definitionFor(refinedReceiverType) : holder)
.asProgramClass();
- assert refinedHolder != null;
+ if (refinedHolder == null) {
+ return null;
+ }
assert !refinedHolder.isInterface();
if (method.isSingleVirtualMethodCached(refinedReceiverType)) {
return method.getSingleVirtualMethodCache(refinedReceiverType);
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 245f3c6..a9f60ec 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1907,6 +1907,12 @@
return info != null && info.isRead();
}
+ public boolean isFieldWrittenInMethodSatisfying(
+ DexEncodedField field, Predicate<DexEncodedMethod> predicate) {
+ FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
+ return info != null && info.isWrittenInMethodSatisfying(predicate);
+ }
+
public boolean isFieldWrittenOutsideDefaultConstructor(DexEncodedField field) {
FieldAccessInfoImpl info = fieldAccessInfoCollection.get(field.field);
if (info == null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index f1e06d1..0dcf1eb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -261,7 +261,7 @@
attributes.add(SOURCE_DEBUG_EXTENSION);
}
if (runtimeVisibleAnnotations) {
- attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS);
+ attributes.add(RUNTIME_VISIBLE_ANNOTATIONS);
}
if (runtimeInvisibleAnnotations) {
attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS);
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 dc998e2..e815737 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -183,9 +183,9 @@
// Flag to toggle if DEX code objects should pass-through without IR processing.
public boolean passthroughDexCode = false;
- // TODO(b/134705306): Currently allow merging dex files resulting from Java 8 library
- // desugaring until all D8 users are complient.
- public boolean enableNeverMergePrefixes = false;
+
+ // Flag to toggle if the prefix based merge restriction should be enforced.
+ public boolean enableNeverMergePrefixes = true;
public Set<String> neverMergePrefixes = ImmutableSet.of("j$.");
public boolean libraryInterfacesMayHaveStaticInitialization = false;
@@ -246,6 +246,8 @@
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
public boolean enableValuePropagation = true;
+ // TODO(b/125282093): Enable member value propagation for instance fields.
+ public boolean enableValuePropagationForInstanceFields = false;
public boolean enableUninstantiatedTypeOptimization = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
@@ -391,6 +393,8 @@
public boolean readCompileTimeAnnotations = true;
public List<String> logArgumentsFilter = ImmutableList.of();
+ // Flag to turn on/offLoad/store optimization in the Cf back-end.
+ public boolean enableLoadStoreOptimization = true;
// Flag to turn on/off lambda class merging in R8.
public boolean enableLambdaMerging = false;
// Flag to turn on/off desugaring in D8/R8.
@@ -1007,7 +1011,6 @@
public Set<Inliner.Reason> validInliningReasons = null;
public boolean noLocalsTableOnInput = false;
public boolean forceNameReflectionOptimization = false;
- public boolean disallowLoadStoreOptimization = false;
public boolean enableNarrowingChecksInD8 = false;
public Consumer<IRCode> irModifier = null;
// TODO(b/129458850) When fixed, remove this and change all usages to "true".
diff --git a/src/main/keep.txt b/src/main/keep.txt
index d52b8f0..b0c58c9 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -25,3 +25,6 @@
# Compatibility command line program used by the Android Platform build.
-keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
+
+# Compatibility command line program used by in google3.
+-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
\ No newline at end of file
diff --git a/src/test/examples/shaking18/Options.java b/src/test/examples/shaking18/Options.java
index edb9d77..7012340 100644
--- a/src/test/examples/shaking18/Options.java
+++ b/src/test/examples/shaking18/Options.java
@@ -4,9 +4,7 @@
package shaking18;
public class Options {
- // TODO(b/138913138): member value propagation can behave same with and without initialization.
- // public boolean alwaysFalse = false;
- public boolean alwaysFalse;
+ public boolean alwaysFalse = false;
public boolean dummy = false;
public Options() {}
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index c913b40..d740e54 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.D8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 268709d..2b6cd3c 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -3,20 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.ToolHelper.KT_COMPILER;
-import static com.android.tools.r8.ToolHelper.KT_PRELOADER;
import static com.android.tools.r8.ToolHelper.isWindows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.ZipUtils;
import java.io.IOException;
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.Collection;
@@ -26,32 +25,59 @@
public class KotlinCompilerTool {
+ public static final class KotlinCompiler {
+
+ private final String name;
+ private final Path path;
+
+ public KotlinCompiler(String name, Path path) {
+ this.name = name;
+ this.path = path;
+ }
+
+ public Path getPath() {
+ return path;
+ }
+ }
+
+ public static KotlinCompiler KOTLINC =
+ new KotlinCompiler(
+ "kotlinc",
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "kotlin", "kotlinc", "lib", "kotlin-compiler.jar"));
+
private final CfRuntime jdk;
private final TestState state;
- private final String kotlincJar;
+ private final KotlinCompiler compiler;
+ private final KotlinTargetVersion targetVersion;
private final List<Path> sources = new ArrayList<>();
private final List<Path> classpath = new ArrayList<>();
private Path output = null;
- private KotlinCompilerTool(CfRuntime jdk, TestState state, Path kotlincJar) {
+ private KotlinCompilerTool(
+ CfRuntime jdk, TestState state, KotlinCompiler compiler, KotlinTargetVersion targetVersion) {
this.jdk = jdk;
this.state = state;
- this.kotlincJar = kotlincJar == null ? KT_COMPILER : kotlincJar.toString();
+ this.compiler = compiler;
+ this.targetVersion = targetVersion;
}
- public static KotlinCompilerTool create(CfRuntime jdk, TemporaryFolder temp) {
- return create(jdk, new TestState(temp), null);
+ public static KotlinCompilerTool create(
+ CfRuntime jdk,
+ TemporaryFolder temp,
+ KotlinCompiler kotlinCompiler,
+ KotlinTargetVersion kotlinTargetVersion) {
+ return create(jdk, new TestState(temp), kotlinCompiler, kotlinTargetVersion);
}
- public static KotlinCompilerTool create (CfRuntime jdk, TemporaryFolder temp, Path kotlincJar) {
- return create(jdk, new TestState(temp), kotlincJar);
+ public static KotlinCompilerTool create(
+ CfRuntime jdk,
+ TestState state,
+ KotlinCompiler kotlinCompiler,
+ KotlinTargetVersion kotlinTargetVersion) {
+ return new KotlinCompilerTool(jdk, state, kotlinCompiler, kotlinTargetVersion);
}
- public static KotlinCompilerTool create(CfRuntime jdk, TestState state, Path kotlincJar) {
- return new KotlinCompilerTool(jdk, state, kotlincJar);
- }
-
- public KotlinCompilerTool addSourceFiles(Path files) {
+ public KotlinCompilerTool addSourceFiles(Path... files) {
return addSourceFiles(Arrays.asList(files));
}
@@ -78,9 +104,7 @@
}
private Path getOrCreateOutputPath() throws IOException {
- return output != null
- ? output
- : state.getNewTempFolder().resolve("out.jar");
+ return output != null ? output : state.getNewTempFolder().resolve("out.jar");
}
/** Compile and return the compilations process result object. */
@@ -98,15 +122,15 @@
}
private ProcessResult compileInternal(Path output) throws IOException {
- Path outdir = Files.isDirectory(output) ? output : state.getNewTempFolder();
List<String> cmdline = new ArrayList<>();
cmdline.add(jdk.getJavaExecutable().toString());
- cmdline.add("-jar");
- cmdline.add(KT_PRELOADER);
- cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
cmdline.add("-cp");
- cmdline.add(kotlincJar);
+ cmdline.add(compiler.getPath().toString());
cmdline.add(ToolHelper.K2JVMCompiler);
+ cmdline.add("-jdk-home");
+ cmdline.add(jdk.getJavaHome().toString());
+ cmdline.add("-jvm-target");
+ cmdline.add(targetVersion.getJvmTargetString());
for (Path source : sources) {
cmdline.add(source.toString());
}
@@ -120,11 +144,6 @@
.collect(Collectors.joining(isWindows() ? ";" : ":")));
}
ProcessBuilder builder = new ProcessBuilder(cmdline);
- ProcessResult kotlincResult = ToolHelper.runProcess(builder);
- if (FileUtils.isJarFile(output)) {
- assert !outdir.equals(output);
- ZipUtils.zip(output, outdir);
- }
- return kotlincResult;
+ return ToolHelper.runProcess(builder);
}
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index a483cfa..8e3e149 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 023e0e7..57eae6b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -11,9 +11,11 @@
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.DataResourceProvider.Visitor;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.dex.ApplicationReader;
@@ -206,16 +208,9 @@
return JavaCompilerTool.create(jdk, temp);
}
- public KotlinCompilerTool kotlinc(CfRuntime jdk) {
- return KotlinCompilerTool.create(jdk, temp);
- }
-
- public KotlinCompilerTool kotlinc(CfRuntime jdk, Path kotlincJar) {
- return KotlinCompilerTool.create(jdk, temp, kotlincJar);
- }
-
- public static KotlinCompilerTool kotlinc(CfRuntime jdk, TemporaryFolder temp) {
- return KotlinCompilerTool.create(jdk, temp);
+ public KotlinCompilerTool kotlinc(
+ CfRuntime jdk, KotlinCompiler kotlinCompiler, KotlinTargetVersion kotlinTargetVersion) {
+ return KotlinCompilerTool.create(jdk, temp, kotlinCompiler, kotlinTargetVersion);
}
public static ClassFileTransformer transformer(Class<?> clazz) throws IOException {
@@ -1365,4 +1360,22 @@
public JarBuilder jarBuilder() throws IOException {
return JarBuilder.builder(temp);
}
+
+ public Collection<Path> buildOnDexRuntime(TestParameters parameters, Collection<Path> paths)
+ throws CompilationFailedException, IOException {
+ if (parameters.isCfRuntime()) {
+ return paths;
+ }
+ return Collections.singletonList(
+ testForD8()
+ .addProgramFiles(paths)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip());
+ }
+
+ public Collection<Path> buildOnDexRuntime(TestParameters parameters, Path... paths)
+ throws IOException, CompilationFailedException {
+ return buildOnDexRuntime(parameters, Arrays.asList(paths));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5d8e69b..af1b81e 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase.KeepRuleConsumer;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 64c32a0..c0a5ee0 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AssemblyWriter;
import com.android.tools.r8.graph.DexApplication;
@@ -73,6 +74,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.CRC32;
import org.junit.Assert;
import org.junit.Assume;
@@ -165,9 +167,10 @@
Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map");
public static final Path DEPS_NOT_RELOCATED = Paths.get(LIBS_DIR, "deps-not-relocated.jar");
+ public static final Path DESUGAR_LIB_CONVERSIONS =
+ Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
public static final Path DESUGAR_LIB_JSON_FOR_TESTING =
- Paths.get(
- "src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugar_jdk_libs.json");
+ Paths.get("src/library_desugar/desugar_jdk_libs.json");
public static boolean isLocalDevelopment() {
return System.getProperty("local_development", "0").equals("1");
@@ -2065,6 +2068,17 @@
public String getFolderName() {
return folderName;
}
+
+ public String getJvmTargetString() {
+ switch (this) {
+ case JAVA_6:
+ return "1.6";
+ case JAVA_8:
+ return "1.8";
+ default:
+ throw new Unimplemented("JvmTarget not specified for " + this);
+ }
+ }
}
public static void disassemble(AndroidApp app, PrintStream ps)
@@ -2073,4 +2087,20 @@
new ApplicationReader(app, new InternalOptions(), new Timing()).read().toDirect();
new AssemblyWriter(application, new InternalOptions(), true, false).write(ps);
}
+
+ public static Path getTestFolderForClass(Class<?> clazz) {
+ return Paths.get(ToolHelper.TESTS_DIR)
+ .resolve("java")
+ .resolve(ToolHelper.getFileNameForTestClass(clazz))
+ .getParent();
+ }
+
+ public static Collection<Path> getFilesInTestFolderRelativeToClass(
+ Class<?> clazz, String folderName, String endsWith) throws IOException {
+ Path subFolder = getTestFolderForClass(clazz).resolve(folderName);
+ assert Files.isDirectory(subFolder);
+ try (Stream<Path> walker = Files.walk(subFolder)) {
+ return walker.filter(path -> path.toString().endsWith(endsWith)).collect(Collectors.toList());
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index cf8f3f4..f659d1c 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -41,7 +41,7 @@
.noMinification()
.noTreeShaking()
.enableInliningAnnotations()
- .addOptionsModification(o -> o.testing.disallowLoadStoreOptimization = true)
+ .addOptionsModification(o -> o.enableLoadStoreOptimization = false)
.run(TryRangeTest.class)
.assertSuccessWithOutput(StringUtils.lines("10", "7.0"));
}
@@ -58,7 +58,7 @@
.enableInliningAnnotations()
.addOptionsModification(
o -> {
- o.testing.disallowLoadStoreOptimization = true;
+ o.enableLoadStoreOptimization = false;
o.testing.irModifier = this::processIR;
})
.run(TryRangeTestLimitRange.class)
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
index c7a6bc0..a1e32b4 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -3,12 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.bootstrap;
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.internal.CompilationTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -50,7 +53,7 @@
public void testForRuntime() throws Exception {
// Compile Hello.kt and make sure it works as expected.
Path classPathBefore =
- kotlinc(parameters.getRuntime().asCf())
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
.addSourceFiles(HELLO_KT)
.setOutputPath(temp.newFolder().toPath())
.compile();
@@ -63,7 +66,6 @@
@Ignore(
"b/136457753: assertion error in static class merger; "
- + "b/144861881: force-inlining of non-inlineable constructors in vertical class merger; "
+ "b/144877828: assertion error in method naming state during interface method renaming; "
+ "b/144859533: umbrella"
)
@@ -101,8 +103,6 @@
o.ignoreMissingClasses = true;
// b/144861100: invoke-static on interface is allowed up to JDK 8.
o.testing.allowInvokeErrors = true;
- // TODO(b/144861881): force-inlining of non-inlineable constructors.
- o.enableVerticalClassMerging = false;
})
.compile()
.writeToZip();
@@ -117,7 +117,10 @@
// TODO(b/144859533): passing `dir` as -kotlin-home.
// Compile Hello.kt again with r8-processed kotlin-compiler.jar
Path classPathAfter =
- kotlinc(parameters.getRuntime().asCf(), r8ProcessedKotlinc)
+ kotlinc(
+ parameters.getRuntime().asCf(),
+ new KotlinCompiler("r8ProcessedKotlinc", r8ProcessedKotlinc),
+ KotlinTargetVersion.JAVA_8)
.addSourceFiles(HELLO_KT)
.setOutputPath(temp.newFolder().toPath())
.compile();
diff --git a/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java b/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java
new file mode 100644
index 0000000..06344db
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/ForceInlineConstructorWithMultiPackageAccessesTest.java
@@ -0,0 +1,75 @@
+// 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.classmerging;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+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.classmerging.testclasses.ForceInlineConstructorWithMultiPackageAccessesTestClasses;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForceInlineConstructorWithMultiPackageAccessesTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ForceInlineConstructorWithMultiPackageAccessesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ForceInlineConstructorWithMultiPackageAccessesTest.class)
+ .addInnerClasses(ForceInlineConstructorWithMultiPackageAccessesTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .enableMergeAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ // Check that C is present to ensure that B is not only removed as a result of the entire
+ // object allocation being removed.
+ assertThat(inspector.clazz(C.class), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(new C());
+ }
+ }
+
+ static class B extends ForceInlineConstructorWithMultiPackageAccessesTestClasses.A {
+
+ // B.<init>() invokes protected A.<init>() and accesses package-private field `greeting`.
+ String greeting = System.currentTimeMillis() >= 0 ? "Hello world!" : null;
+ }
+
+ static class C extends B {
+
+ @Override
+ public String toString() {
+ return greeting;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java
new file mode 100644
index 0000000..80ab1d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/testclasses/ForceInlineConstructorWithMultiPackageAccessesTestClasses.java
@@ -0,0 +1,16 @@
+// 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.classmerging.testclasses;
+
+import com.android.tools.r8.NeverMerge;
+
+public class ForceInlineConstructorWithMultiPackageAccessesTestClasses {
+
+ @NeverMerge
+ public static class A {
+
+ protected A() {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
new file mode 100644
index 0000000..4ec0131
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -0,0 +1,216 @@
+// 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.compatdx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class CompatDxTests {
+ private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
+
+ private static final String EXAMPLE_JAR_FILE1 = ToolHelper.EXAMPLES_BUILD_DIR + "arithmetic.jar";
+ private static final String EXAMPLE_JAR_FILE2 = ToolHelper.EXAMPLES_BUILD_DIR + "barray.jar";
+
+ private static final String NO_LOCALS = "--no-locals";
+ private static final String NO_POSITIONS = "--positions=none";
+ private static final String MULTIDEX = "--multi-dex";
+ private static final String NUM_THREADS_5 = "--num-threads=5";
+
+ @Rule
+ public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void noFilesTest() throws IOException {
+ runDexer("--no-files");
+ }
+
+ @Test
+ public void noOutputTest() throws IOException {
+ runDexerWithoutOutput(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void singleJarInputFile() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void multipleJarInputFiles() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1, EXAMPLE_JAR_FILE2);
+ }
+
+ @Test
+ public void outputZipFile() throws IOException {
+ runDexerWithOutput("foo.dex.zip", NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void useMultipleThreads() throws IOException {
+ runDexer(NUM_THREADS_5, NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withPositions() throws IOException {
+ runDexer(NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withLocals() throws IOException {
+ runDexer(NO_POSITIONS, MULTIDEX, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void withoutMultidex() throws IOException {
+ runDexer(NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void writeToNamedDexFile() throws IOException {
+ runDexerWithOutput("named-output.dex", EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void keepClassesSingleDexTest() throws IOException {
+ runDexerWithOutput("out.zip", "--keep-classes", EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void keepClassesMultiDexTest() throws IOException {
+ runDexerWithOutput("out.zip", "--keep-classes", "--multi-dex", EXAMPLE_JAR_FILE1);
+ }
+
+ @Test
+ public void ignoreDexInArchiveTest() throws IOException {
+ // Create a JAR with both a .class and a .dex file (the .dex file is just empty).
+ Path jarWithClassesAndDex = temp.newFile("test.jar").toPath();
+ Files.copy(Paths.get(EXAMPLE_JAR_FILE1), jarWithClassesAndDex,
+ StandardCopyOption.REPLACE_EXISTING);
+ URI uri = URI.create("jar:" + jarWithClassesAndDex.toUri());
+ FileSystem fileSystem = FileSystems.newFileSystem(uri, ImmutableMap.of("create", "true"));
+ Path dexFile = fileSystem.getPath("classes.dex");
+ Files.newOutputStream(dexFile, StandardOpenOption.CREATE).close();
+ fileSystem.close();
+
+ // Only test this with CompatDx, as dx does not like the empty .dex file.
+ List<String> d8Args =ImmutableList.of(
+ "--output=" + temp.newFolder("out").toString(), jarWithClassesAndDex.toString());
+ CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
+ }
+
+ private void runDexer(String... args) throws IOException {
+ runDexerWithOutput("", args);
+ }
+
+ private void runDexerWithoutOutput(String... args) throws IOException {
+ runDexerWithOutput(null, args);
+ }
+
+ private Path getOutputD8() {
+ return temp.getRoot().toPath().resolve("d8-out");
+ }
+
+ private Path getOutputDX() {
+ return temp.getRoot().toPath().resolve("dx-out");
+ }
+
+ private void runDexerWithOutput(String out, String... args) throws IOException {
+ Path d8Out = null;
+ Path dxOut = null;
+ if (out != null) {
+ Path baseD8 = getOutputD8();
+ Path baseDX = getOutputDX();
+ Files.createDirectory(baseD8);
+ Files.createDirectory(baseDX);
+ d8Out = baseD8.resolve(out);
+ dxOut = baseDX.resolve(out);
+ assertNotEquals(d8Out, dxOut);
+ }
+
+ List<String> d8Args = new ArrayList<>(args.length + 2);
+ d8Args.add("--dex");
+ if (d8Out != null) {
+ d8Args.add("--output=" + d8Out);
+ }
+ Collections.addAll(d8Args, args);
+ System.out.println("running: d8 " + StringUtils.join(d8Args, " "));
+ CompatDx.main(d8Args.toArray(StringUtils.EMPTY_ARRAY));
+
+ List<String> dxArgs = new ArrayList<>(args.length + 2);
+ if (dxOut != null) {
+ dxArgs.add("--output=" + dxOut);
+ }
+ Collections.addAll(dxArgs, args);
+ System.out.println("running: dx " + StringUtils.join(dxArgs, " "));
+ ProcessResult result = ToolHelper.runDX(dxArgs.toArray(StringUtils.EMPTY_ARRAY));
+ assertEquals(result.stderr, 0, result.exitCode);
+
+ if (out == null) {
+ // Can't check output if explicitly not writing any.
+ return;
+ }
+
+ List<Path> d8Files = Files.list(Files.isDirectory(d8Out) ? d8Out : d8Out.getParent())
+ .sorted().collect(Collectors.toList());
+ List<Path> dxFiles = Files.list(Files.isDirectory(dxOut) ? dxOut : dxOut.getParent())
+ .sorted().collect(Collectors.toList());
+ assertEquals("Out file names differ",
+ StringUtils.join(dxFiles, "\n", BraceType.NONE, (file) ->
+ file.getFileName().toString()),
+ StringUtils.join(d8Files, "\n", BraceType.NONE, (file) ->
+ file.getFileName().toString()));
+
+ for (int i = 0; i < d8Files.size(); i++) {
+ if (FileUtils.isArchive(d8Files.get(i))) {
+ compareArchiveFiles(d8Files.get(i), dxFiles.get(i));
+ }
+ }
+ }
+
+ private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
+ ZipFile d8Zip = new ZipFile(d8File.toFile(), StandardCharsets.UTF_8);
+ ZipFile dxZip = new ZipFile(dxFile.toFile(), StandardCharsets.UTF_8);
+ // TODO(zerny): This should test resource containment too once supported.
+ Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+ Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+ for (String entry : d8Content) {
+ assertTrue("Expected dx output to contain " + entry, dxContent.contains(entry));
+ }
+ for (String entry : dxContent) {
+ Path path = Paths.get(entry);
+ if (FileUtils.isDexFile(path) || FileUtils.isClassFile(path)) {
+ assertTrue("Expected d8 output to contain " + entry, d8Content.contains(entry));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
index 5b5b420..eb22ea9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -17,7 +17,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class CustomCollectionSuperCallsTest extends CoreLibDesugarTestBase {
+public class CustomCollectionSuperCallsTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index ae8012a..6085be7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.APIConversionTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -32,7 +31,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class CustomCollectionTest extends APIConversionTestBase {
+public class CustomCollectionTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
@@ -65,7 +64,7 @@
this.assertCustomCollectionCallsCorrect(inspector, false);
})
.addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension,
+ this::buildDesugaredLibrary,
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java
new file mode 100644
index 0000000..220147f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java
@@ -0,0 +1,159 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.hamcrest.CoreMatchers.containsString;
+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.TestRunResult;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library interface conflicts with a
+ * default method in a program interface, then deriving both leads to a ICCE.
+ *
+ * <p>In contrast to DefaultMethodOverrideConflictWithLibraryTest, in this test, the conflict is in
+ * a class with the two conflicting interfaces as the immediate interfaces.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideConflictWithLibrary2Test extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public DefaultMethodOverrideConflictWithLibrary2Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private List<Class<?>> getClasses() {
+ return ImmutableList.of(
+ Main.class,
+ MyIntegerList.class,
+ MyIntegerArrayListWithOverride.class,
+ MyHasCharacteristics.class);
+ }
+
+ private List<byte[]> getTransforms() throws IOException {
+ return ImmutableList.of(
+ transformer(MySpliterator.class)
+ .setImplements(Spliterator.class, MyHasCharacteristics.class)
+ .transform());
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+ if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+ // This test assumes that the library defines default method Spliterator::hasCharacteristics.
+ Method method = Spliterator.class.getDeclaredMethod("hasCharacteristics", int.class);
+ assertNotNull(method);
+ assertTrue(method.isDefault());
+ }
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(getClasses())
+ .addProgramClassFileData(getTransforms())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResult);
+ } else {
+ testForD8()
+ .addProgramClasses(getClasses())
+ .addProgramClassFileData(getTransforms())
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .disassemble()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResult);
+ }
+ }
+
+ private void checkResult(TestRunResult<?> result) {
+ if (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm().equals(CfVm.JDK11)) {
+ // TODO(b/145566657): For some reason JDK11 throws AbstractMethodError.
+ result.assertFailureWithErrorThatMatches(containsString(AbstractMethodError.class.getName()));
+ } else {
+ result.assertFailureWithErrorThatMatches(
+ containsString(IncompatibleClassChangeError.class.getName()));
+ }
+ }
+
+ // Interface that will lead to a conflicting default method with Spliterator::hasCharacteristics.
+ interface MyHasCharacteristics {
+
+ default boolean hasCharacteristics(int characteristics) {
+ System.out.println("MyHasCharacteristics::hasCharacteristics");
+ return false;
+ }
+ }
+
+ // Class deriving the default methods. The resolution of which will throw ICCE.
+ static class MySpliterator implements Spliterator<Integer> /*, MyHasCharacteristics via ASM */ {
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Integer> action) {
+ return false;
+ }
+
+ @Override
+ public Spliterator<Integer> trySplit() {
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return 0;
+ }
+
+ @Override
+ public int characteristics() {
+ return 0;
+ }
+ }
+
+ // Custom list interface with a default method for spliterator.
+ interface MyIntegerList extends List<Integer> {
+
+ @Override
+ default Spliterator<Integer> spliterator() {
+ return new MySpliterator();
+ }
+ }
+
+ // Derived list with override of spliterator that will explicitly call the custom default method.
+ static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
+
+ @Override
+ public Spliterator<Integer> spliterator() {
+ return MyIntegerList.super.spliterator();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new MyIntegerArrayListWithOverride().spliterator().hasCharacteristics(0));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java
new file mode 100644
index 0000000..d244909
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibraryTest.java
@@ -0,0 +1,117 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is not overridden by a class
+ * method in the library, then multiple maximally specific method lead to ICCE.
+ *
+ * <p>Concretely, Collection defines a default removeIf() method which is not overridden in either
+ * the List interface or the LinkedList class. Thus, a class that has an unrelated default method
+ * for removeIf will cause a conflict throwing ICCE.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideConflictWithLibraryTest extends DesugaredLibraryTestBase {
+
+ private static List<Class<?>> CLASSES = ImmutableList.of(Main.class, MyRemoveIf.class);
+
+ private static List<byte[]> getTransforms() throws IOException {
+ return ImmutableList.of(
+ transformer(ConflictingClass.class).setImplements(MyRemoveIf.class).transform());
+ }
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public DefaultMethodOverrideConflictWithLibraryTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+ if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+ // This test assumes that the library defines a Collection class with a default removeIf.
+ Method removeIf = Collection.class.getDeclaredMethod("removeIf", Predicate.class);
+ assertNotNull(removeIf);
+ assertTrue(removeIf.isDefault());
+ // Also, the LinkedList implementation does *not* define an override of removeIf.
+ try {
+ LinkedList.class.getDeclaredMethod("removeIf", Predicate.class);
+ fail("Unexpected defintion of removeIf on LinkedList");
+ } catch (NoSuchMethodException e) {
+ // Expected.
+ }
+ }
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(getTransforms())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(getExpectedError());
+ } else {
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(getTransforms())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(getExpectedError());
+ }
+ }
+
+ private Matcher<String> getExpectedError() {
+ return containsString(IncompatibleClassChangeError.class.getName());
+ }
+
+ // Interface with a default method that can lead to a conflict.
+ interface MyRemoveIf {
+
+ default boolean removeIf(Predicate<? super Integer> filter) {
+ System.out.println("MyRemoveIf::removeIf");
+ return false;
+ }
+ }
+
+ // Derived list with no override of removeIf but with a default method in MyRemoveIf.
+ // The two maximally specific methods Collection::removeIf and MyRemoveIf must cause ICCE.
+ static class ConflictingClass extends LinkedList<Integer> /* implements MyRemoveIf via ASM */ {
+ // Intentionally empty.
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new ConflictingClass().removeIf(e -> false));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
new file mode 100644
index 0000000..7716c96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
@@ -0,0 +1,161 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is overridden by a class method
+ * also in the library, then that class method remains the target of resolution and dispatch.
+ *
+ * <p>Concretely, List (and Collection) define a default spliterator() method which is overridden in
+ * the ArrayList class. Thus, any class deriving ArrayList for which spliterator is not overridden
+ * should end up targeting that of ArrayList and not other potential non-library default interface
+ * methods.
+ */
+@RunWith(Parameterized.class)
+public class DefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
+
+ static final String EXPECTED = StringUtils.lines("0", "42");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public DefaultMethodOverrideInLibraryTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+ if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+ // This test assumes that the library defines an ArrayList class with a declared spliterator.
+ // For that reason, resolution will find that definition prior to a default interface method.
+ Method spliterator = ArrayList.class.getDeclaredMethod("spliterator");
+ assertNotNull(spliterator);
+ assertFalse(spliterator.isDefault());
+ }
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addInnerClasses(DefaultMethodOverrideInLibraryTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED);
+ } else {
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(DefaultMethodOverrideInLibraryTest.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResult);
+ }
+ }
+
+ private void checkResult(D8TestRunResult result) {
+ // TODO(b/145506767): Default method desugaring fails to generate a library forwarding method.
+ if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ result.assertFailureWithErrorThatMatches(
+ containsString(
+ parameters
+ .getRuntime()
+ .asDex()
+ .getVm()
+ .getVersion()
+ .isOlderThanOrEqual(Version.V4_4_4)
+ ? "VerifyError"
+ : AbstractMethodError.class.getName()));
+ return;
+ }
+ // TODO(b/145504401): Execution on Art 7.0.0 has the wrong runtime behavior.
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().getVersion().equals(Version.V7_0_0)) {
+ result.assertSuccessWithOutputLines("42", "42");
+ return;
+ }
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+
+ // Custom spliterator, just returns 42 in estimateSize, otherwise unused.
+ static class MySpliterator implements Spliterator<Integer> {
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Integer> action) {
+ return false;
+ }
+
+ @Override
+ public Spliterator<Integer> trySplit() {
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return 42; // Overridden to differ from the default.
+ }
+
+ @Override
+ public int characteristics() {
+ return 0;
+ }
+ }
+
+ // Custom list interface with a default method for spliterator.
+ interface MyIntegerList extends List<Integer> {
+
+ @Override
+ default Spliterator<Integer> spliterator() {
+ return new MySpliterator();
+ }
+ }
+
+ // Derived list with no override of spliterator. The call will thus go to the super class, not
+ // the default method!
+ static class MyIntegerArrayListWithoutOverride extends ArrayList<Integer>
+ implements MyIntegerList {
+ // No override of spliterator.
+ }
+
+ // Derived list with an override of spliterator. The call must hit the classes override and that
+ // will explictly call the custom default method.
+ static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
+
+ @Override
+ public Spliterator<Integer> spliterator() {
+ return MyIntegerList.super.spliterator();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new MyIntegerArrayListWithoutOverride().spliterator().estimateSize());
+ System.out.println(new MyIntegerArrayListWithOverride().spliterator().estimateSize());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index df86791..159aa8d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -16,8 +16,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -28,7 +26,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class DesugaredLibraryContentTest extends CoreLibDesugarTestBase {
+public class DesugaredLibraryContentTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@@ -79,18 +77,7 @@
}
private void assertCorrect(CodeInspector inspector) {
- inspector
- .allClasses()
- .forEach(
- clazz ->
- assertTrue(
- clazz.getOriginalName().startsWith("j$.")
- || clazz
- .getOriginalName()
- .startsWith(DesugaredLibraryWrapperSynthesizer.WRAPPER_PREFIX)
- || clazz
- .getOriginalName()
- .contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)));
+ inspector.allClasses().forEach(clazz -> assertTrue(clazz.getOriginalName().startsWith("j$.")));
assertThat(inspector.clazz("j$.time.Clock"), isPresent());
// Above N the following classes are removed instead of being desugared.
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
similarity index 90%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index e176fca..17b2a61 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CoreLibDesugarTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,10 +24,11 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class CoreLibDesugarTestBase extends TestBase {
+public class DesugaredLibraryTestBase extends TestBase {
protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
@@ -37,37 +38,39 @@
return parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel();
}
- protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel) throws RuntimeException {
+ protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel) {
return buildDesugaredLibrary(apiLevel, "", false);
}
- protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules)
- throws RuntimeException {
+ protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules) {
return buildDesugaredLibrary(apiLevel, keepRules, true);
}
- protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink)
- throws RuntimeException {
+ protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink) {
return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of());
}
protected Path buildDesugaredLibrary(
- AndroidApiLevel apiLevel, String keepRules, boolean shrink, List<Path> additionalProgramFiles)
- throws RuntimeException {
+ AndroidApiLevel apiLevel,
+ String keepRules,
+ boolean shrink,
+ List<Path> additionalProgramFiles) {
// We wrap exceptions in a RuntimeException to call this from a lambda.
try {
// If we compile extended library here, it means we use TestNG.
// TestNG requires annotations, hence we disable AnnotationRemoval.
// This implies that extra warning are generated if this is set.
boolean disableL8AnnotationRemovalForTesting = !additionalProgramFiles.isEmpty();
+ ArrayList<Path> extraPaths = new ArrayList<>(additionalProgramFiles);
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
L8Command.Builder l8Builder =
L8Command.builder(diagnosticsHandler)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
.setMode(shrink ? CompilationMode.RELEASE : CompilationMode.DEBUG)
- .addProgramFiles(additionalProgramFiles)
+ .addProgramFiles(extraPaths)
.addDesugaredLibraryConfiguration(
StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
.setMinApiLevel(apiLevel.getLevel())
@@ -95,6 +98,10 @@
}
return desugaredLib;
} catch (Exception e) {
+ // Don't wrap assumption violation so junit can catch it.
+ if (e instanceof RuntimeException) {
+ throw ((RuntimeException) e);
+ }
throw new RuntimeException(e);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
index 5cc9006..3cf6d11 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
@@ -19,7 +19,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class DisableDesugarTest extends CoreLibDesugarTestBase {
+public class DisableDesugarTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
index 6a59664..3b12474 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DoubleUtilityClassTest.java
@@ -19,7 +19,7 @@
// In this test both the desugared library and the program have the same utility class.
@RunWith(Parameterized.class)
-public class DoubleUtilityClassTest extends CoreLibDesugarTestBase {
+public class DoubleUtilityClassTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
index 80bdb4f..3abe853 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
@@ -22,7 +22,7 @@
import java.util.zip.ZipFile;
import org.junit.Test;
-public class EmptyDesugaredLibrary extends CoreLibDesugarTestBase {
+public class EmptyDesugaredLibrary extends DesugaredLibraryTestBase {
private L8Command.Builder prepareL8Builder(AndroidApiLevel minApiLevel) {
return L8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
index c2cc678..5851353 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
@@ -29,19 +29,19 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class EmulatedInterfacesTest extends CoreLibDesugarTestBase {
+public class EmulatedInterfacesTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
- private final boolean shrinkCoreLibrary;
+ private final boolean shrinkDesugaredLibrary;
- @Parameters(name = "{1}, shrinkCoreLibrary: {0}")
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
public EmulatedInterfacesTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
- this.shrinkCoreLibrary = shrinkDesugaredLibrary;
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -51,7 +51,7 @@
CodeInspector inspector =
new CodeInspector(
buildDesugaredLibrary(
- parameters.getApiLevel(), "-keep class **$-EL", shrinkCoreLibrary));
+ parameters.getApiLevel(), "-keep class **$-EL", shrinkDesugaredLibrary));
assertEmulateInterfaceClassesPresentWithDispatchMethods(inspector);
assertCollectionMethodsPresentWithCorrectDispatch(inspector);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 5c295c9..732c0b9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -23,7 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class JavaTimeTest extends CoreLibDesugarTestBase {
+public class JavaTimeTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
index 78617c7..7ca5301 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilFunctionTest.java
@@ -23,7 +23,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class JavaUtilFunctionTest extends CoreLibDesugarTestBase {
+public class JavaUtilFunctionTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
index 31a7413..c2f83eb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaUtilOptionalTest.java
@@ -29,7 +29,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class JavaUtilOptionalTest extends CoreLibDesugarTestBase {
+public class JavaUtilOptionalTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
index e9f221d..bbadc1d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
@@ -15,7 +15,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class LinkedHashSetTest extends CoreLibDesugarTestBase {
+public class LinkedHashSetTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
index 6303b04..2275a44 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -13,13 +13,13 @@
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.desugaredlibraryjdktests.Jdk11CoreLibTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
import org.junit.Test;
-public class MergingJ$Test extends Jdk11CoreLibTestBase {
+public class MergingJ$Test extends Jdk11DesugaredLibraryTestBase {
@Test
public void testMergingJ$() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
index a865ee3..e2a5d8d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NeverMergeCoreLibDesugarClasses.java
@@ -21,7 +21,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class NeverMergeCoreLibDesugarClasses extends CoreLibDesugarTestBase {
+public class NeverMergeCoreLibDesugarClasses extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@@ -46,7 +46,6 @@
.addInnerClasses(NeverMergeCoreLibDesugarClasses.class)
.addProgramDexFileData(builder.compile())
.setMinApi(parameters.getRuntime())
- .addOptionsModification(options -> options.enableNeverMergePrefixes = true)
.compileWithExpectedDiagnostics(diagnostics -> {
diagnostics.assertErrorsCount(1);
String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
@@ -72,7 +71,6 @@
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.setMinApi(parameters.getRuntime())
.addProgramFiles(buildDesugaredLibrary(parameters.getApiLevel()))
- .addOptionsModification(options -> options.enableNeverMergePrefixes = true)
.compileWithExpectedDiagnostics(diagnostics -> {
diagnostics.assertErrorsCount(1);
String message = diagnostics.getErrors().get(0).getDiagnosticMessage();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java
new file mode 100644
index 0000000..d9f93b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDefaultMethodOverrideInLibraryTest.java
@@ -0,0 +1,118 @@
+// 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.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Predicate;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * This test checks that if a default interface method in a library is not overridden by a class
+ * method in the library, then a program defined maximally specific method becomes the target if
+ * present.
+ *
+ * <p>Concretely, Collection defines a default removeIf() method which is not overridden in either
+ * the List interface or the LinkedList class. Thus, any class deriving LinkedList for which
+ * removeIf is overridden or a new default method is added should target those extensions.
+ */
+@RunWith(Parameterized.class)
+public class NoDefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
+
+ static final String EXPECTED = StringUtils.lines("MyIntegerList::removeIf", "false", "false");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public NoDefaultMethodOverrideInLibraryTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestRuntime systemRuntime = TestRuntime.getSystemRuntime();
+ if (systemRuntime.isCf() && systemRuntime.asCf().isNewerThanOrEqual(CfVm.JDK8)) {
+ // This test assumes that the library defines a Collection class with a default removeIf.
+ Method removeIf = Collection.class.getDeclaredMethod("removeIf", Predicate.class);
+ assertNotNull(removeIf);
+ assertTrue(removeIf.isDefault());
+ // Also, the LinkedList implementation does *not* define an override of removeIf.
+ try {
+ LinkedList.class.getDeclaredMethod("removeIf", Predicate.class);
+ fail("Unexpected defintion of removeIf on LinkedList");
+ } catch (NoSuchMethodException e) {
+ // Expected.
+ }
+ }
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addInnerClasses(NoDefaultMethodOverrideInLibraryTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED);
+ } else {
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(NoDefaultMethodOverrideInLibraryTest.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+ }
+
+ // Custom list interface with a default method for removeIf.
+ interface MyIntegerList extends List<Integer> {
+
+ @Override
+ default boolean removeIf(Predicate<? super Integer> filter) {
+ System.out.println("MyIntegerList::removeIf");
+ return false;
+ }
+ }
+
+ // Derived list with no override of removeIf but with a default method in MyIntegerList.
+ // The call will thus go to the maximally specific method, which is MyIntegerList::removeIf.
+ static class MyIntegerLinkedListWithoutOverride extends LinkedList<Integer>
+ implements MyIntegerList {
+ // No override of spliterator.
+ }
+
+ // Derived list with an override of removeIf. The call must hit the classes override and that
+ // will explictly call the library default method (Collection.removeIf).
+ static class MyIntegerLinkedListWithOverride extends LinkedList<Integer>
+ implements MyIntegerList {
+
+ @Override
+ public boolean removeIf(Predicate<? super Integer> filter) {
+ return super.removeIf(filter);
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new MyIntegerLinkedListWithoutOverride().removeIf(e -> false));
+ System.out.println(new MyIntegerLinkedListWithOverride().removeIf(e -> false));
+ }
+ }
+}
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 59db52d..69f366f 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
@@ -34,7 +34,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ProgramRewritingTest extends CoreLibDesugarTestBase {
+public class ProgramRewritingTest extends DesugaredLibraryTestBase {
private static final String TEST_CLASS = "stream.ProgramRewritingTestClass";
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index b20a716..eebea28 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -18,7 +18,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class RetargetOverrideTest extends CoreLibDesugarTestBase {
+public class RetargetOverrideTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
index af7d1c8..725bf3f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/StaticInterfaceMethodTest.java
@@ -15,7 +15,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class StaticInterfaceMethodTest extends CoreLibDesugarTestBase {
+public class StaticInterfaceMethodTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
index a8bf8ed..403ae3c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassErrorTest.java
@@ -9,11 +9,12 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.time.Year;
import org.junit.Test;
-public class APIConversionFinalClassErrorTest extends APIConversionTestBase {
+public class APIConversionFinalClassErrorTest extends DesugaredLibraryTestBase {
@Test
public void testFinalMethod() {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
index 6d7a9d5..fe0e1a5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionLargeWarningTest.java
@@ -6,6 +6,7 @@
import static org.hamcrest.CoreMatchers.startsWith;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.file.Path;
import java.time.Clock;
@@ -13,7 +14,7 @@
import java.util.stream.Stream;
import org.junit.Test;
-public class APIConversionLargeWarningTest extends APIConversionTestBase {
+public class APIConversionLargeWarningTest extends DesugaredLibraryTestBase {
@Test
public void testFinalMethod() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
index c45b403..4c2007f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
@@ -21,7 +22,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class APIConversionTest extends APIConversionTestBase {
+public class APIConversionTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@@ -60,8 +61,7 @@
.enableCoreLibraryDesugaring(parameters.getApiLevel())
.compile()
.assertOnlyInfos() // No warnings.
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
.run(parameters.getRuntime(), Executor.class)
.assertSuccessWithOutput(
StringUtils.lines(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
deleted file mode 100644
index 29b316d..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionTestBase.java
+++ /dev/null
@@ -1,68 +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.desugar.desugaredlibrary.conversiontests;
-
-import com.android.tools.r8.TestRuntime;
-import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import org.junit.Assume;
-
-public class APIConversionTestBase extends CoreLibDesugarTestBase {
-
- private static final Path CONVERSION_FOLDER = Paths.get("src/test/desugaredLibrary");
-
- public Path[] getConversionClasses() throws IOException {
- Assume.assumeTrue(
- "JDK8 javac is required to avoid dealing with modules and JDK8 is not checked-in on"
- + " windows",
- !ToolHelper.isWindows());
-
- CfRuntime runtime = TestRuntime.getCheckedInJdk8();
- Path conversionFolder = temp.newFolder("conversions").toPath();
-
- // Compile the stubs to be able to compile the conversions.
- Path stubsJar =
- javac(runtime)
- .addSourceFiles(
- getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs"), "java"))
- .compile();
-
- // Compile the conversions using the stubs.
- javac(runtime)
- .setOutputPath(conversionFolder)
- .addClasspathFiles(stubsJar)
- .addSourceFiles(
- getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("conversions"), "java"))
- .compile();
-
- Path[] classes = getAllFilesWithSuffixInDirectory(conversionFolder, "class");
- assert classes.length > 0
- : "Something went wrong during compilation, check the runJavac return value for debugging.";
- return classes;
- }
-
- protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel) {
- return buildDesugaredLibraryWithConversionExtension(apiLevel, "", false);
- }
-
- protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel,String keepRules, boolean shrink) {
- Path[] timeConversionClasses;
- try {
- timeConversionClasses = getConversionClasses();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- ArrayList<Path> paths = new ArrayList<>();
- Collections.addAll(paths, timeConversionClasses);
- return buildDesugaredLibrary(apiLevel, keepRules, shrink, paths);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
index 9eda3e2..ef30ad1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllOptionalConversionTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -15,7 +16,7 @@
import java.util.OptionalLong;
import org.junit.Test;
-public class AllOptionalConversionTest extends APIConversionTestBase {
+public class AllOptionalConversionTest extends DesugaredLibraryTestBase {
@Test
public void testRewrittenAPICalls() throws Exception {
@@ -26,8 +27,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index 1128076..db5c661 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -22,7 +23,7 @@
import java.time.ZonedDateTime;
import org.junit.Test;
-public class AllTimeConversionTest extends APIConversionTestBase {
+public class AllTimeConversionTest extends DesugaredLibraryTestBase {
@Test
public void testRewrittenAPICalls() throws Exception {
@@ -36,8 +37,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile();
compileResult
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
index f87454c..f31ad69 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicLongDoubleConversionTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -15,7 +16,7 @@
// Longs and double take two stack indexes, this had to be dealt with in
// CfAPIConverter*WrapperCodeProvider (See stackIndex vs index), this class tests that the
// synthetic Cf code is correct.
-public class BasicLongDoubleConversionTest extends APIConversionTestBase {
+public class BasicLongDoubleConversionTest extends DesugaredLibraryTestBase {
@Test
public void testRewrittenAPICalls() throws Exception {
@@ -26,8 +27,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(StringUtils.lines("--01-16"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
index 7c45daa..1e66f26 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/BasicTimeConversionTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -25,7 +26,7 @@
import java.util.TimeZone;
import org.junit.Test;
-public class BasicTimeConversionTest extends APIConversionTestBase {
+public class BasicTimeConversionTest extends DesugaredLibraryTestBase {
@Test
public void testTimeGeneratedDex() throws Exception {
@@ -34,7 +35,7 @@
L8Command.Builder l8Builder =
L8Command.builder(diagnosticsHandler)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .addProgramFiles(getConversionClasses())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
.addDesugaredLibraryConfiguration(
StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
.setMinApiLevel(AndroidApiLevel.B.getLevel())
@@ -57,8 +58,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::checkAPIRewritten)
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
index 2bdc252..38e8bac 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -18,7 +19,7 @@
import java.util.function.Consumer;
import org.junit.Test;
-public class CallBackConversionTest extends APIConversionTestBase {
+public class CallBackConversionTest extends DesugaredLibraryTestBase {
@Test
public void testCallBack() throws Exception {
@@ -57,8 +58,7 @@
.toString()
.equals("java.util.function.Consumer")));
})
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
.assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
@@ -76,8 +76,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::assertLibraryOverridesThere)
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
.assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
@@ -94,8 +93,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::assertLibraryOverridesThere)
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Impl.class)
.assertSuccessWithOutput(StringUtils.lines("0", "1", "0", "1"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
index 17751e9..d4c7f53 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ClockAPIConversionTest.java
@@ -6,13 +6,14 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.time.Clock;
import org.junit.Test;
-public class ClockAPIConversionTest extends APIConversionTestBase {
+public class ClockAPIConversionTest extends DesugaredLibraryTestBase {
@Test
public void testClock() throws Exception {
@@ -23,8 +24,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(StringUtils.lines("Z", "Z", "true"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
index 66d1a72..bdd9f5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionErrorMessageTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
@@ -17,7 +18,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ConversionErrorMessageTest extends APIConversionTestBase {
+public class ConversionErrorMessageTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@@ -78,8 +79,7 @@
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel())
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
.run(parameters.getRuntime(), Executor.class)
.assertSuccessWithOutput(getExpectedResult());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 11dcdeb..b13ade6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -15,7 +16,7 @@
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
-public class ConversionIntroduceInterfaceMethodTest extends APIConversionTestBase {
+public class ConversionIntroduceInterfaceMethodTest extends DesugaredLibraryTestBase {
@Test
public void testNoInterfaceMethods() throws Exception {
@@ -30,8 +31,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
index 79cad2f..1f433d4 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -18,7 +19,7 @@
import java.util.function.BiConsumer;
import org.junit.Test;
-public class DuplicateAPIDesugaredLibTest extends APIConversionTestBase {
+public class DuplicateAPIDesugaredLibTest extends DesugaredLibraryTestBase {
@Test
public void testLib() throws Exception {
@@ -38,7 +39,7 @@
.compile()
.addDesugaredCoreLibraryRunClassPath(
(AndroidApiLevel api) -> {
- desugaredLibBox.set(this.buildDesugaredLibraryWithConversionExtension(api));
+ desugaredLibBox.set(this.buildDesugaredLibrary(api));
return desugaredLibBox.get();
},
AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
index 31ca3b5..2699bd3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -16,7 +17,7 @@
import java.util.function.BiConsumer;
import org.junit.Test;
-public class DuplicateAPIProgramTest extends APIConversionTestBase {
+public class DuplicateAPIProgramTest extends DesugaredLibraryTestBase {
@Test
public void testMap() throws Exception {
@@ -29,8 +30,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::assertDupMethod)
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 61068b3..1dfbc73 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
@@ -28,7 +29,7 @@
import org.junit.Assert;
import org.junit.Test;
-public class FunctionConversionTest extends APIConversionTestBase {
+public class FunctionConversionTest extends DesugaredLibraryTestBase {
@Test
public void testFunctionComposition() throws Exception {
@@ -41,8 +42,7 @@
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::assertSingleWrappers)
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
index 393ebca..eea96ae 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/MoreFunctionConversionTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -19,7 +20,7 @@
import java.util.function.Function;
import org.junit.Test;
-public class MoreFunctionConversionTest extends APIConversionTestBase {
+public class MoreFunctionConversionTest extends DesugaredLibraryTestBase {
@Test
public void testFunction() throws Exception {
@@ -34,11 +35,10 @@
Path program = compileResult.writeToZip();
assertNoDuplicateLambdas(program, customLib);
compileResult
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
- .assertSuccessWithOutput(StringUtils.lines("6","6","6","6","6"));
+ .assertSuccessWithOutput(StringUtils.lines("6", "6", "6", "6", "6"));
}
// If we have the exact same lambda in both, but one implements j$..Function and the other
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
index 3e65742..bebefd1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.util.Arrays;
@@ -19,7 +20,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SummaryStatisticsConversionTest extends APIConversionTestBase {
+public class SummaryStatisticsConversionTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@@ -50,8 +51,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel())
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
.addRunClasspathFiles(customLib)
.run(parameters.getRuntime(), Executor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
index 9ff6fa8..3589bb3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
@@ -6,13 +6,14 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.time.ZoneId;
import org.junit.Test;
-public class TryCatchTimeConversionTest extends APIConversionTestBase {
+public class TryCatchTimeConversionTest extends DesugaredLibraryTestBase {
@Test
public void testBaseline() throws Exception {
@@ -23,8 +24,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), BaselineExecutor.class)
.assertSuccessWithOutput(StringUtils.lines("GMT", "GMT", "GMT", "GMT", "GMT"));
@@ -39,8 +39,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), TryCatchExecutor.class)
.assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
index 43ede3a..646d20f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -13,7 +14,7 @@
import java.util.function.IntConsumer;
import org.junit.Test;
-public class UnwrapConversionTest extends APIConversionTestBase {
+public class UnwrapConversionTest extends DesugaredLibraryTestBase {
@Test
public void testUnwrap() throws Exception {
@@ -24,8 +25,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
.assertSuccessWithOutput(StringUtils.lines("true", "true"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
index bb0c2b1..26f4629 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeConflictTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
@@ -18,7 +19,7 @@
import java.util.function.IntSupplier;
import org.junit.Test;
-public class WrapperMergeConflictTest extends APIConversionTestBase {
+public class WrapperMergeConflictTest extends DesugaredLibraryTestBase {
@Test
public void testWrapperMergeConflict() throws Exception {
@@ -50,8 +51,7 @@
.addProgramFiles(path1, path2)
.addLibraryClasses(CustomLibClass.class)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.inspect(this::assertBigWrappersPresent)
.addRunClasspathFiles(customLib)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor1.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
index c93e820..4c1caf7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
@@ -16,7 +17,7 @@
import java.util.Arrays;
import org.junit.Test;
-public class WrapperMergeTest extends APIConversionTestBase {
+public class WrapperMergeTest extends DesugaredLibraryTestBase {
@Test
public void testWrapperMerge() throws Exception {
@@ -36,14 +37,11 @@
.inspect(this::assertWrappers)
.writeToZip();
testForD8()
- .addProgramFiles(path1,path2)
+ .addProgramFiles(path1, path2)
.compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, AndroidApiLevel.B)
.run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor1.class)
- .assertSuccessWithOutput(
- StringUtils.lines("[1, 2, 3]"));
-
+ .assertSuccessWithOutput(StringUtils.lines("[1, 2, 3]"));
}
private void assertWrappers(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
index d2501e9..b161386 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -27,7 +27,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class Jdk11AtomicTests extends Jdk11CoreLibTestBase {
+public class Jdk11AtomicTests extends Jdk11DesugaredLibraryTestBase {
private static final Path ATOMIC_TESTS_FOLDER =
Paths.get(ToolHelper.JDK_11_TESTS_DIR + "java/util/concurrent/atomic/");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
index ff779f5..fa595cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -31,7 +31,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class Jdk11ConcurrentMapTests extends Jdk11CoreLibTestBase {
+public class Jdk11ConcurrentMapTests extends Jdk11DesugaredLibraryTestBase {
private static final Path CONCURRENT_TESTS_FOLDER =
Paths.get(ToolHelper.JDK_11_TESTS_DIR + "java/util/concurrent/ConcurrentMap/");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
similarity index 94%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
index b3e6156..94c128e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11CoreLibTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11DesugaredLibraryTestBase.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -10,7 +10,7 @@
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.File;
@@ -24,7 +24,7 @@
// Provides convenience to use Paths/SafeVarargs which are missing on old Android but
// required by some Jdk tests, and for java.base extensions.
-public class Jdk11CoreLibTestBase extends CoreLibDesugarTestBase {
+public class Jdk11DesugaredLibraryTestBase extends DesugaredLibraryTestBase {
protected static Path[] JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES;
protected static final Path JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR =
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
index 69c3558..713061e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11MathTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
index c8a3e2d..c24839f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11ObjectsTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ObjectsTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index d885b0f..433f56a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
@@ -37,7 +37,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class Jdk11StreamTests extends Jdk11CoreLibTestBase {
+public class Jdk11StreamTests extends Jdk11DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
index 58acdc7..736acc5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/desugaredlibraryjdktests/Jdk11TimeTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeTests.java
@@ -2,7 +2,7 @@
// 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.desugaredlibraryjdktests;
+package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static org.junit.Assert.assertTrue;
@@ -21,7 +21,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class Jdk11TimeTests extends Jdk11CoreLibTestBase {
+public class Jdk11TimeTests extends Jdk11DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
index 819e6ce..91d0e98 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.APIConversionTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
@@ -33,7 +33,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class HelloWorldCompiledOnArtTest extends APIConversionTestBase {
+public class HelloWorldCompiledOnArtTest extends DesugaredLibraryTestBase {
// TODO(b/142621961): Create an abstraction to easily run tests on External DexR8.
// Manage pathMock in the abstraction.
@@ -136,8 +136,7 @@
|| diagnosticMessages.getWarnings().stream()
.noneMatch(x -> x.getDiagnosticMessage().contains("andThen")));
return compile
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibraryWithConversionExtension, parameters.getApiLevel())
+ .addDesugaredCoreLibraryRunClassPath(this::buildDesugaredLibrary, parameters.getApiLevel())
.withArt6Plus64BitsLib()
.withArtFrameworks();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index 2533619..02623a8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -27,7 +27,7 @@
// TODO(b/142621961): Parametrize at least L and P instead of just P.
@RunWith(Parameterized.class)
-public class R8CompiledThroughDexTest extends CoreLibDesugarTestBase {
+public class R8CompiledThroughDexTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
index 6ee2045..454adc7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/EnumSetTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.DayOfWeek;
@@ -19,7 +19,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class EnumSetTest extends CoreLibDesugarTestBase {
+public class EnumSetTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
index a0ad904..5211d3d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/FieldAccessTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.ZoneId;
@@ -16,7 +16,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class FieldAccessTest extends CoreLibDesugarTestBase {
+public class FieldAccessTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
index 9d5f8e0..a6061c8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/InheritanceTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.shrinkingtests;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.time.Clock;
@@ -18,7 +18,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InheritanceTest extends CoreLibDesugarTestBase {
+public class InheritanceTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
index d289490..2a673bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/shrinkingtests/KeepRuleShrinkTest.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.Map;
@@ -19,7 +19,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class KeepRuleShrinkTest extends CoreLibDesugarTestBase {
+public class KeepRuleShrinkTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java
new file mode 100644
index 0000000..3a501a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/AvoidInliningRecursiveMethodTest.java
@@ -0,0 +1,79 @@
+// 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.inliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AvoidInliningRecursiveMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public AvoidInliningRecursiveMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(AvoidInliningRecursiveMethodTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject mainMethodSubject = classSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+
+ // TODO(b/145276800): Should not inline recursive methods.
+ assertTrue(mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isIf));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(recursive(getInt()));
+ }
+
+ @NeverInline
+ static int getInt() {
+ return System.currentTimeMillis() >= 0 ? 42 : 0;
+ }
+
+ static String recursive(int x) {
+ if (x > 0) {
+ return recursive(x - 1);
+ }
+ return "Hello world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
index 39872ae..beb56a7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineNonReboundFieldTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.inliner;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -39,9 +40,9 @@
// since main() does not have access to the GreetingBase.greeting field.
assertThat(greeterSubject.uniqueMethodWithName("greet"), isPresent());
- // TODO(b/128967328): The method greetInternal() should be inlined into greet() since it has a
- // single call site and nothing prevents it from being inlined.
- assertThat(greeterSubject.uniqueMethodWithName("greetInternal"), isPresent());
+ // The method greetInternal() should be inlined into greet() since it has a single call site and
+ // nothing prevents it from being inlined.
+ assertThat(greeterSubject.uniqueMethodWithName("greetInternal"), not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
new file mode 100644
index 0000000..58ad103
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -0,0 +1,141 @@
+// 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.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InstanceFieldValuePropagationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InstanceFieldValuePropagationTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(options -> {
+ // TODO(b/125282093): Remove options modification once landed.
+ assert !options.enableValuePropagationForInstanceFields;
+ options.enableValuePropagationForInstanceFields = true;
+ })
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ StringUtils.times(StringUtils.lines("A", "42", "Hello world!"), 2));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ // Verify that all instance-get instructions in testDefinitelyNotNull() has been removed by
+ // member value propagation.
+ MethodSubject testDefinitelyNotNullMethodSubject =
+ testClassSubject.uniqueMethodWithName("testDefinitelyNotNull");
+ assertThat(testDefinitelyNotNullMethodSubject, isPresent());
+ assertTrue(
+ testDefinitelyNotNullMethodSubject
+ .streamInstructions()
+ .noneMatch(InstructionSubject::isInstanceGet));
+ // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+ // ends up being unused.
+ assertTrue(
+ testDefinitelyNotNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isNewInstance));
+
+ // Verify that all instance-get instructions in testMaybeNull() has been removed by member value
+ // propagation.
+ MethodSubject testMaybeNullMethodSubject =
+ testClassSubject.uniqueMethodWithName("testMaybeNull");
+ assertThat(testMaybeNullMethodSubject, isPresent());
+ // TODO(b/125282093): Should synthesize a null-check and still propagate the field values even
+ // when the receiver is nullable.
+ assertTrue(
+ testMaybeNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isInstanceGet));
+ // TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
+ // ends up being unused.
+ assertTrue(
+ testMaybeNullMethodSubject
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isNewInstance));
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ // TODO(b/125282093): Need to remove the instance-put instructions in A.<init>(). This can not
+ // be done safely by the time we process A.<init>(), so some kind of post-processing is needed.
+ assertEquals(3, aClassSubject.allInstanceFields().size());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testDefinitelyNotNull();
+ testMaybeNull();
+ }
+
+ @NeverInline
+ static void testDefinitelyNotNull() {
+ A a = new A();
+ System.out.println(a.e);
+ System.out.println(a.i);
+ System.out.println(a.s);
+ }
+
+ @NeverInline
+ static void testMaybeNull() {
+ A a = System.currentTimeMillis() >= 0 ? new A() : null;
+ System.out.println(a.e);
+ System.out.println(a.i);
+ System.out.println(a.s);
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ MyEnum e = MyEnum.A;
+ int i = 42;
+ String s = "Hello world!";
+ }
+
+ enum MyEnum {
+ A,
+ B
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
new file mode 100644
index 0000000..d2b739e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
@@ -0,0 +1,87 @@
+// 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.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InstanceFieldValuePropagationWithMultipleInstanceInitializersTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InstanceFieldValuePropagationWithMultipleInstanceInitializersTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ // Verify that the instance-get instruction in main() is still present in main(), since the
+ // value of `a.greeting` depends on the constructor being used.
+ MethodSubject mainMethodSubject = testClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isInstanceGet));
+
+ // Verify that the `greeting` field is still present.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject.uniqueFieldWithName("greeting"), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A a = System.currentTimeMillis() >= 0 ? new A() : new A(new Object());
+ System.out.println(a.greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ String greeting;
+
+ A() {
+ this.greeting = "Hello world!";
+ }
+
+ A(Object unused) {
+ this.greeting = ":-(";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
new file mode 100644
index 0000000..b85f583
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
@@ -0,0 +1,294 @@
+// 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.kotlin;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Position;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Result;
+import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Source;
+import com.android.tools.r8.utils.StringUtils;
+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 KotlinSourceDebugExtensionParserTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public KotlinSourceDebugExtensionParserTest(TestParameters parameters) {}
+
+ @Test
+ public void testParsingEmpty() {
+ assertNull(KotlinSourceDebugExtensionParser.parse(null));
+ }
+
+ @Test
+ public void testParsingNoInlineSources() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "EnumSwitch.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 EnumSwitch.kt",
+ "enumswitch/EnumSwitchKt",
+ "*L",
+ "1#1,38:1",
+ "*E");
+ Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
+ assertNotNull(result);
+ assertEquals(1, result.getFiles().size());
+ Source source = result.getFiles().get(1);
+ assertEquals("EnumSwitch.kt", source.getFileName());
+ assertEquals("enumswitch/EnumSwitchKt", source.getPath());
+ assertTrue(result.getPositions().containsKey(1));
+ Position position = result.getPositions().get(1);
+ assertEquals(source, position.getSource());
+ assertEquals(1, position.getRange().from);
+ assertEquals(38, position.getRange().to);
+ }
+
+ @Test
+ public void testParsingSimpleStrata() {
+ // Taken from src/test/examplesKotlin/retrace/mainKt
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#1,22:1",
+ "7#2:23",
+ "12#3:24",
+ "*E",
+ "*S KotlinDebug",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "*L",
+ "12#1:23",
+ "18#1:24",
+ "*E");
+ Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
+ assertNotNull(result);
+ assertEquals(3, result.getFiles().size());
+ // Check that files are correctly parsed.
+ Source source1 = result.getFiles().get(1);
+ assertEquals("Main.kt", source1.getFileName());
+ assertEquals("retrace/MainKt", source1.getPath());
+
+ Source source2 = result.getFiles().get(2);
+ assertEquals("InlineFunction.kt", source2.getFileName());
+ assertEquals("retrace/InlineFunctionKt", source2.getPath());
+
+ Source source3 = result.getFiles().get(3);
+ assertEquals("InlineFunction.kt", source3.getFileName());
+ assertEquals("retrace/InlineFunction", source3.getPath());
+
+ // Check that the inline positions can be traced.
+ assertTrue(result.getPositions().containsKey(23));
+ Position position1 = result.getPositions().get(23);
+ assertEquals(source2, position1.getSource());
+ assertEquals(7, position1.getRange().from);
+ assertEquals(7, position1.getRange().to);
+
+ assertTrue(result.getPositions().containsKey(24));
+ Position position2 = result.getPositions().get(24);
+ assertEquals(source3, position2.getSource());
+ assertEquals(12, position2.getRange().from);
+ assertEquals(12, position2.getRange().to);
+ }
+
+ @Test
+ public void testNoKotlinHeader() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#1,22:1",
+ "7#2:23",
+ "12#3:24",
+ "*E",
+ "*S KotlinDebug",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "*L",
+ "12#1:23",
+ "18#1:24",
+ "*E");
+ assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+ }
+
+ @Test
+ public void testIncompleteFileBlock() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#1,22:1",
+ "7#2:23",
+ "12#3:24",
+ "*E",
+ "*S KotlinDebug",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "*L",
+ "12#1:23",
+ "18#1:24",
+ "*E");
+ assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+ }
+
+ @Test
+ public void testDuplicateFileIndex() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 1 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#1,22:1",
+ "7#2:23",
+ "12#3:24",
+ "*E",
+ "*S KotlinDebug",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "*L",
+ "12#1:23",
+ "18#1:24",
+ "*E");
+ assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+ }
+
+ @Test
+ public void testNoDebugEntries() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*E");
+ assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+ }
+
+ @Test
+ public void testInvalidRanges() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#bar,22:1",
+ "7#2:23",
+ "12#3:foo",
+ "*E");
+ assertNull(KotlinSourceDebugExtensionParser.parse(annotationData));
+ }
+
+ @Test
+ public void testNoSourceFileForEntry() {
+ String annotationData =
+ StringUtils.join(
+ "\n",
+ "SMAP",
+ "Main.kt",
+ "Kotlin",
+ "*S Kotlin",
+ "*F",
+ "+ 1 Main.kt",
+ "retrace/MainKt",
+ "+ 2 InlineFunction.kt",
+ "retrace/InlineFunctionKt",
+ "+ 3 InlineFunction.kt",
+ "retrace/InlineFunction",
+ "*L",
+ "1#1,22:1",
+ "7#2:23",
+ "12#4:24", // <-- non-existing file index
+ "*E");
+ Result parsedResult = KotlinSourceDebugExtensionParser.parse(annotationData);
+ assertNotNull(parsedResult);
+
+ assertEquals(2, parsedResult.getPositions().size());
+ assertTrue(parsedResult.getPositions().containsKey(1));
+ assertTrue(parsedResult.getPositions().containsKey(23));
+ assertFalse(parsedResult.getPositions().containsKey(24));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
index 3ba5ecd..e256e37 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.containsString;
@@ -18,6 +19,7 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.nio.file.Path;
import java.util.Collection;
@@ -70,7 +72,7 @@
// Keep the BKt extension method which requires metadata
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes("*Annotation*")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile();
String pkg = getClass().getPackage().getName();
final String superClassName = pkg + ".extension_lib.Super";
@@ -93,7 +95,7 @@
String appFolder = PKG_PREFIX + "/extension_app";
ProcessResult kotlinTestCompileResult =
- kotlinc(parameters.getRuntime().asCf())
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
.addClasspathFiles(r8ProcessedLibZip)
.addSourceFiles(getKotlinFileInTest(appFolder, "main"))
.setOutputPath(temp.newFolder().toPath())
@@ -117,7 +119,7 @@
// Keep the BKt extension method which requires metadata
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
- .addKeepAttributes("*Annotation*")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile();
String pkg = getClass().getPackage().getName();
final String superClassName = pkg + ".extension_lib.Super";
@@ -136,19 +138,19 @@
assertThat(metadata.toString(), not(containsString("Super")));
});
- Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
- compileResult.writeToZip(r8ProcessedLibZip);
+ Path libJar = temp.newFile("lib.jar").toPath();
+ compileResult.writeToZip(libJar);
String appFolder = PKG_PREFIX + "/extension_app";
Path output =
- kotlinc(parameters.getRuntime().asCf())
- .addClasspathFiles(r8ProcessedLibZip)
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(appFolder, "main"))
.setOutputPath(temp.newFolder().toPath())
.compile();
testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), r8ProcessedLibZip)
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), pkg + ".extension_app.MainKt")
.assertSuccessWithOutputLines("do stuff", "do stuff");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
new file mode 100644
index 0000000..1030339
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
@@ -0,0 +1,110 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInParametertypeTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRenameInParametertypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static Path parameterLibJar;
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String paramLibFolder = PKG_PREFIX + "/parametertype_lib";
+ parameterLibJar = getStaticTemp().newFile("param_lib.jar").toPath();
+ ProcessResult processResult =
+ ToolHelper.runKotlinc(
+ null,
+ parameterLibJar,
+ null,
+ getKotlinFileInTest(paramLibFolder, "lib")
+ );
+ assertEquals(0, processResult.exitCode);
+ }
+
+ @Test
+ public void testMetadataInParameter_renamed() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(parameterLibJar)
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ // Keep Itf, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Itf")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile();
+ String pkg = getClass().getPackage().getName();
+ final String itfClassName = pkg + ".parametertype_lib.Itf";
+ final String implClassName = pkg + ".parametertype_lib.Impl";
+ compileResult.inspect(inspector -> {
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isPresent());
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+ assertNotNull(metadata);
+ // TODO(b/70169921): should not refer to Itf
+ assertThat(metadata.toString(), containsString("Itf"));
+ });
+
+ Path libJar = temp.newFile("lib.jar").toPath();
+ compileResult.writeToZip(libJar);
+
+ String appFolder = PKG_PREFIX + "/parametertype_app";
+ ProcessResult processResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, processResult.exitCode);
+ assertThat(
+ processResult.stderr,
+ containsString("cannot access class '" + pkg + ".parametertype_lib.Itf'"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
new file mode 100644
index 0000000..fe815d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
@@ -0,0 +1,107 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInPropertyTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRenameInPropertyTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static Path propertyLibJar;
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String propertyLibFolder = PKG_PREFIX + "/propertytype_lib";
+ propertyLibJar = getStaticTemp().newFile("property_lib.jar").toPath();
+ ProcessResult processResult =
+ ToolHelper.runKotlinc(
+ null,
+ propertyLibJar,
+ null,
+ getKotlinFileInTest(propertyLibFolder, "lib")
+ );
+ assertEquals(0, processResult.exitCode);
+ }
+
+ @Test
+ public void testMetadataInProperty_renamed() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(propertyLibJar)
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile();
+ String pkg = getClass().getPackage().getName();
+ final String itfClassName = pkg + ".propertytype_lib.Itf";
+ final String implClassName = pkg + ".propertytype_lib.Impl";
+ compileResult.inspect(inspector -> {
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isPresent());
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+ assertNotNull(metadata);
+ // TODO(b/70169921): should not refer to Itf
+ assertThat(metadata.toString(), containsString("Itf"));
+ });
+
+ Path libJar = temp.newFile("lib.jar").toPath();
+ compileResult.writeToZip(libJar);
+
+ String appFolder = PKG_PREFIX + "/propertytype_app";
+ ProcessResult processResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, processResult.exitCode);
+ assertThat(
+ processResult.stderr,
+ containsString("cannot access class '" + pkg + ".propertytype_lib.Itf'"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
new file mode 100644
index 0000000..da6bf76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
@@ -0,0 +1,110 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRenameInReturntypeTest extends KotlinMetadataTestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataRenameInReturntypeTest(
+ TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static Path returntypeLibJar;
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String returntypeLibFolder = PKG_PREFIX + "/returntype_lib";
+ returntypeLibJar = getStaticTemp().newFile("returntype_lib.jar").toPath();
+ ProcessResult processResult =
+ ToolHelper.runKotlinc(
+ null,
+ returntypeLibJar,
+ null,
+ getKotlinFileInTest(returntypeLibFolder, "lib")
+ );
+ assertEquals(0, processResult.exitCode);
+ }
+
+ @Test
+ public void testmetadataInReturnType_renamed() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(returntypeLibJar)
+ // Keep non-private members of Impl
+ .addKeepRules("-keep public class **.Impl { !private *; }")
+ // Keep Itf, but allow minification.
+ .addKeepRules("-keep,allowobfuscation class **.Itf")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile();
+ String pkg = getClass().getPackage().getName();
+ final String itfClassName = pkg + ".returntype_lib.Itf";
+ final String implClassName = pkg + ".returntype_lib.Impl";
+ compileResult.inspect(inspector -> {
+ ClassSubject itf = inspector.clazz(itfClassName);
+ assertThat(itf, isPresent());
+ assertThat(itf, isRenamed());
+
+ ClassSubject impl = inspector.clazz(implClassName);
+ assertThat(impl, isPresent());
+ assertThat(impl, not(isRenamed()));
+ // API entry is kept, hence the presence of Metadata.
+ DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
+ assertNotNull(metadata);
+ // TODO(b/70169921): should not refer to Itf
+ assertThat(metadata.toString(), containsString("Itf"));
+ });
+
+ Path libJar = temp.newFile("lib.jar").toPath();
+ compileResult.writeToZip(libJar);
+
+ String appFolder = PKG_PREFIX + "/returntype_app";
+ ProcessResult processResult =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ // TODO(b/70169921): update to just .compile() once fixed.
+ .compileRaw();
+ // TODO(b/70169921): should be able to compile!
+ assertNotEquals(0, processResult.exitCode);
+ assertThat(
+ processResult.stderr,
+ containsString("cannot access class '" + pkg + ".returntype_lib.Itf'"));
+ }
+}
+
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
index 7001909..dc29f2c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.containsString;
@@ -17,6 +18,7 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.nio.file.Path;
import java.util.Collection;
@@ -66,7 +68,7 @@
.addProgramFiles(supertypeLibJar)
// Keep non-private members except for ones in `internal` definitions.
.addKeepRules("-keep public class !**.internal.**, * { !private *; }")
- .addKeepAttributes("*Annotation*")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile();
String pkg = getClass().getPackage().getName();
final String itfClassName = pkg + ".supertype_lib.internal.Itf";
@@ -90,7 +92,7 @@
String appFolder = PKG_PREFIX + "/supertype_app";
Path output =
- kotlinc(parameters.getRuntime().asCf())
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
.addClasspathFiles(r8ProcessedLibZip)
.addSourceFiles(getKotlinFileInTest(appFolder, "main"))
.setOutputPath(temp.newFolder().toPath())
@@ -112,7 +114,7 @@
.addKeepRules("-keep public class !**.internal.**, * { !private *; }")
// Keep `internal` definitions, but allow minification.
.addKeepRules("-keep,allowobfuscation class **.internal.** { *; }")
- .addKeepAttributes("*Annotation*")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile();
String pkg = getClass().getPackage().getName();
final String itfClassName = pkg + ".supertype_lib.internal.Itf";
@@ -133,19 +135,19 @@
assertThat(metadata.toString(), containsString("a/a"));
});
- Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
- compileResult.writeToZip(r8ProcessedLibZip);
+ Path libJar = temp.newFile("lib.jar").toPath();
+ compileResult.writeToZip(libJar);
String appFolder = PKG_PREFIX + "/supertype_app";
Path output =
- kotlinc(parameters.getRuntime().asCf())
- .addClasspathFiles(r8ProcessedLibZip)
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(appFolder, "main"))
.setOutputPath(temp.newFolder().toPath())
.compile();
testForJvm()
- .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), r8ProcessedLibZip)
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
.assertSuccessWithOutputLines("Impl::foo", "Program::foo");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index fa46140..00b5606 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.Collection;
@@ -49,7 +50,7 @@
.addProgramFiles(getJavaJarFile(folder))
.addProgramFiles(ToolHelper.getKotlinReflectJar())
.addKeepMainRule(mainClassName)
- .addKeepAttributes("*Annotation*")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addKeepRules("-keep class kotlin.Metadata")
// TODO(b/145090972): Should never need to exit gracefully during testing.
.allowClassInlinerGracefulExit()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt
new file mode 100644
index 0000000..17a6457
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_app/main.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.metadata.parametertype_app
+
+import com.android.tools.r8.kotlin.metadata.parametertype_lib.Impl
+
+class ProgramClass : Impl() {
+ override fun bar() {
+ super.bar()
+ println("Program::bar")
+ }
+}
+
+fun main() {
+ val instance = ProgramClass()
+ instance.foo(instance)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt
new file mode 100644
index 0000000..75c3d78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/parametertype_lib/lib.kt
@@ -0,0 +1,19 @@
+// 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.kotlin.metadata.parametertype_lib
+
+interface Itf {
+ fun foo(arg : Itf)
+ fun bar()
+}
+
+open class Impl : Itf {
+ override fun foo(arg : Itf) {
+ arg.bar()
+ }
+
+ override fun bar() {
+ println("Impl::bar")
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt
new file mode 100644
index 0000000..214300c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_app/main.kt
@@ -0,0 +1,14 @@
+// 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.kotlin.metadata.propertytype_app
+
+import com.android.tools.r8.kotlin.metadata.propertytype_lib.Impl
+
+class ProgramClass : Impl(8) {
+}
+
+fun main() {
+ val instance : Impl = ProgramClass()
+ println(instance.prop1)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt
new file mode 100644
index 0000000..40aa801
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/propertytype_lib/lib.kt
@@ -0,0 +1,18 @@
+// 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.kotlin.metadata.propertytype_lib
+
+interface Itf {
+ val prop1: Itf
+}
+
+open class Impl(val id: Int) : Itf {
+
+ override val prop1: Itf
+ get() = this
+
+ override fun toString(): String {
+ return "Impl::$id"
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
new file mode 100644
index 0000000..37d446e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_app/main.kt
@@ -0,0 +1,20 @@
+// 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.kotlin.metadata.returntype_app
+
+import com.android.tools.r8.kotlin.metadata.returntype_lib.Impl
+import com.android.tools.r8.kotlin.metadata.returntype_lib.Itf
+
+class ProgramClass : Impl() {
+ override fun foo(): Itf {
+ super.foo()
+ println("Program::foo")
+ return this
+ }
+}
+
+fun main() {
+ val instance = ProgramClass()
+ println(instance == instance.foo())
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt
new file mode 100644
index 0000000..c24e292
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/returntype_lib/lib.kt
@@ -0,0 +1,15 @@
+// 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.kotlin.metadata.returntype_lib
+
+interface Itf {
+ fun foo() : Itf
+}
+
+open class Impl : Itf {
+ override fun foo() : Itf {
+ println("Impl::foo")
+ return this
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
index 4192774..309b756 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_app/main.kt
@@ -12,6 +12,6 @@
}
}
-fun main(args: Array<String>) {
+fun main() {
ProgramClass().foo()
}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
new file mode 100644
index 0000000..db1864c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
@@ -0,0 +1,113 @@
+// 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.resolution;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LibraryExtendsProgramRefinedReceiverIsLibraryClass extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // Only test on CF, as the test use additional runtime classpath classes.
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ // Regression test for b/145645482. Resolution issue related to lookupSingleVirtualTarget for
+ // library extending program. The concrete issue hit in the Android Platform build this was in
+ // a debug build, where the "debug write" instruction introduces an "alias" causing the
+ // "receiver lower bound" to be unknown (null). With a receiver type of ProgramClass and a
+ // "refined receiver type" of LibraryClass (lattice type of the debug write instruction) the
+ // issue appeared.
+ //
+ // However, with a phi the same could happen in release mode. Again the "refined receiver type"
+ // becomes LibraryClass (lattice type of the phi).
+
+ public LibraryExtendsProgramRefinedReceiverIsLibraryClass(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void regression145645482DebugMode() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addLibraryFiles(runtimeJar(parameters.getBackend()))
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithoutPhi.class)
+ .enableInliningAnnotations()
+ .addKeepClassRules(ProgramClass.class)
+ .addKeepMainRule(ProgramTestRunnerWithoutPhi.class)
+ .setMinApi(parameters.getApiLevel())
+ .debug()
+ .compile()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), ProgramTestRunnerWithoutPhi.class)
+ .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+ }
+
+ @Test
+ public void regression145645482ReleaseMode() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addLibraryFiles(runtimeJar(parameters.getBackend()))
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithPhi.class)
+ .enableInliningAnnotations()
+ .addKeepClassRules(ProgramClass.class)
+ .addKeepMainRule(ProgramTestRunnerWithPhi.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), ProgramTestRunnerWithPhi.class)
+ .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+ }
+
+ static class ProgramClass {
+ public void addTestSuite(Class<?> suite) {
+ }
+ }
+
+ static class LibraryClass extends ProgramClass {
+ @Override
+ public void addTestSuite(Class<?> suite) {
+ super.addTestSuite(suite);
+ }
+ }
+
+ static class ProgramTestRunnerWithoutPhi {
+ @NeverInline
+ public static ProgramClass test() {
+ ProgramClass suite = new LibraryClass();
+ suite.addTestSuite(ProgramTestRunnerWithoutPhi.class);
+ return suite;
+ }
+
+ public static void main(String[] args) {
+ ProgramTestRunnerWithoutPhi.test();
+ System.out.println("SUCCESS");
+ }
+ }
+
+ static class ProgramTestRunnerWithPhi {
+ @NeverInline
+ public static ProgramClass test(ProgramClass otherSuite) {
+ ProgramClass suite = System.currentTimeMillis() > 0 ? new LibraryClass(): otherSuite;
+ suite.addTestSuite(ProgramTestRunnerWithPhi.class);
+ return suite;
+ }
+
+ public static void main(String[] args) {
+ ProgramTestRunnerWithPhi.test(null);
+ System.out.println("SUCCESS");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
new file mode 100644
index 0000000..cba0f8b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -0,0 +1,137 @@
+// 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.retrace;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 KotlinInlineFunctionRetraceTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // TODO(b/141817471): Extend with compilation modes.
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public KotlinInlineFunctionRetraceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+ testForRuntime(parameters)
+ .addProgramFiles(
+ kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addSourceFiles(
+ getFilesInTestFolderRelativeToClass(
+ KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+ .compile())
+ .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar()))
+ .run(parameters.getRuntime(), "retrace.MainKt")
+ .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+ .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:15)"));
+ }
+
+ @Test
+ public void testRetraceKotlinInlineStaticFunction()
+ throws ExecutionException, CompilationFailedException, IOException {
+ String main = "retrace.MainKt";
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(
+ kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addSourceFiles(
+ getFilesInTestFolderRelativeToClass(
+ KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+ .compile())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addKeepAttributes("SourceFile", "LineNumberTable")
+ .setMode(CompilationMode.RELEASE)
+ .addKeepMainRule(main)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), main)
+ .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+ .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:2)"));
+ List<String> retrace = result.retrace();
+ // TODO(b/141817471): Change the tracing information when solved.
+ // assertThat(retrace.get(1), containsString("at retrace.MainKt.main(Main.kt:15)"));
+ }
+
+ @Test
+ public void testRetraceKotlinInlineInstanceFunction()
+ throws ExecutionException, CompilationFailedException, IOException {
+ String main = "retrace.MainInstanceKt";
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(
+ kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addSourceFiles(
+ getFilesInTestFolderRelativeToClass(
+ KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+ .compile())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addKeepAttributes("SourceFile", "LineNumberTable")
+ .setMode(CompilationMode.RELEASE)
+ .addKeepMainRule(main)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), main)
+ .assertFailureWithErrorThatMatches(containsString("inlineExceptionInstance"))
+ .assertFailureWithErrorThatMatches(
+ containsString("at retrace.MainInstanceKt.main(MainInstance.kt:2)"));
+ List<String> retrace = result.retrace();
+ // TODO(b/141817471): Change the tracing information when solved.
+ // assertThat(retrace.get(1), containsString("at
+ // retrace.MainInstanceKt.main(MainInstance.kt:13)"));
+ }
+
+ @Test
+ public void testRetraceKotlinNestedInlineFunction()
+ throws ExecutionException, CompilationFailedException, IOException {
+ // TODO(b/141817471): Change the tracing information when solved.
+ int lineNumber = parameters.isCfRuntime() ? 4 : 3;
+ String main = "retrace.MainNestedKt";
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(
+ kotlinc(TestRuntime.getCheckedInJdk8(), KOTLINC, KotlinTargetVersion.JAVA_8)
+ .addSourceFiles(
+ getFilesInTestFolderRelativeToClass(
+ KotlinInlineFunctionRetraceTest.class, "kt", ".kt"))
+ .compile())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addKeepAttributes("SourceFile", "LineNumberTable")
+ .setMode(CompilationMode.RELEASE)
+ .addKeepMainRule(main)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), main)
+ .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic"))
+ .assertFailureWithErrorThatMatches(
+ containsString("at retrace.MainNestedKt.main(MainNested.kt:" + lineNumber + ")"));
+ List<String> retrace = result.retrace();
+ // TODO(b/141817471): Change the tracing information when solved.
+ // assertThat(retrace.get(1), containsString("at retrace.MainNestedKt.main(MainNested.kt:19)"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt b/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt
new file mode 100644
index 0000000..ceb2656
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/InlineFunction.kt
@@ -0,0 +1,18 @@
+// 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 retrace
+
+inline fun inlineExceptionStatic(f: () -> Unit) {
+ println("in inlineExceptionStatic")
+ throw java.lang.Exception("inlineExceptionStatic")
+ println("will not be printed")
+}
+
+class InlineFunction {
+ inline fun inlineExceptionInstance(f: () -> Unit) {
+ println("in inlineExceptionInstance")
+ throw java.lang.Exception("inlineExceptionInstance")
+ println("will not be printed")
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/Main.kt b/src/test/java/com/android/tools/r8/retrace/kt/Main.kt
new file mode 100644
index 0000000..1c97080
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/Main.kt
@@ -0,0 +1,12 @@
+// 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 retrace
+
+// Adding a few spaces to better see where the debug information is positioned.
+
+fun main(args: Array<String>) {
+ inlineExceptionStatic {
+ throw Exception("Never get's here")
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt
new file mode 100644
index 0000000..38a781f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainInstance.kt
@@ -0,0 +1,10 @@
+// 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 retrace
+
+fun main(args: Array<String>) {
+ InlineFunction().inlineExceptionInstance {
+ throw Exception("Never get's here")
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt
new file mode 100644
index 0000000..f8329be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainNested.kt
@@ -0,0 +1,14 @@
+// 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 retrace
+
+// Some spaces to better see if retrace is working as expected.
+
+
+fun main(args: Array<String>) {
+ nestedInline {
+ throw Exception("Never get's here")
+ }
+}
+
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt b/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt
new file mode 100644
index 0000000..c968da4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/MainWithMultipleInlines.kt
@@ -0,0 +1,18 @@
+// 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 retrace
+
+fun main(args: Array<String>) {
+ println("Before")
+ inlineExceptionStatic {
+ throw Exception("Never get's here")
+ }
+ println("Middle")
+ inlineExceptionStatic {
+ throw Exception("Never get's here")
+ }
+ println("After")
+}
+
+
diff --git a/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt b/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt
new file mode 100644
index 0000000..d8e4736
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/kt/NestedInlineFunction.kt
@@ -0,0 +1,11 @@
+// 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 retrace
+
+inline fun nestedInline(f: () -> Unit) {
+ println("in nestedInline")
+ inlineExceptionStatic(f)
+ println("will never be printed")
+}
+
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 5d110e4..94c6be6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -48,6 +48,10 @@
private void configure(InternalOptions options) {
options.enableEnumValueOptimization = enableOptimization;
+
+ // TODO(b/125282093): Remove options modification once landed.
+ assert !options.enableValuePropagationForInstanceFields;
+ options.enableValuePropagationForInstanceFields = true;
}
@Test
@@ -82,6 +86,7 @@
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inlined"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
+ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1);
} else {
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -89,12 +94,12 @@
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("inSwitch"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
@Test
@@ -125,12 +130,14 @@
assertNameReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), expectedConst);
assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+ assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
} else {
assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("local"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("multipleUsages"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
// TODO(jakew) this should be allowed!
@@ -138,7 +145,6 @@
assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
@Test
@@ -178,6 +184,7 @@
assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO");
assertToStringReplacedWithConst(
clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
+ assertToStringReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
} else {
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("noToString"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -185,11 +192,11 @@
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("inlined"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("differentTypeStaticField"));
+ assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
assertToStringWasNotReplaced(clazz.uniqueMethodWithName("phi"));
- assertToStringWasNotReplaced(clazz.uniqueMethodWithName("nonStaticGet"));
}
private static void assertOrdinalReplacedWithConst(MethodSubject method, int expectedConst) {
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
index ea96ead..3985648 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -51,48 +51,61 @@
testForR8(parameters.getBackend())
.addInnerClasses(EffectivelyFinalInstanceFieldsTest.class)
.addKeepMainRule(MAIN)
+ .addOptionsModification(
+ options -> {
+ // TODO(b/125282093): Remove options modification once landed.
+ assert !options.enableValuePropagationForInstanceFields;
+ options.enableValuePropagationForInstanceFields = true;
+ })
.enableInliningAnnotations()
.enableClassInliningAnnotations()
.enableMergeAnnotations()
.setMinApi(parameters.getRuntime())
.compile()
- .inspect(codeInspector -> {
- ClassSubject main = codeInspector.clazz(MAIN);
- assertThat(main, isPresent());
+ .inspect(
+ codeInspector -> {
+ ClassSubject main = codeInspector.clazz(MAIN);
+ assertThat(main, isPresent());
- MethodSubject mainMethod = main.mainMethod();
- assertThat(mainMethod, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
- // TODO(b/138913138): not trivial; assigned only once in <init>
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
- // TODO(b/138913138): not trivial; assigned multiple times, but within a certain range.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
- assertTrue(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
- // TODO(b/138913138): effectively final, and default value is set.
- assertFalse(
- mainMethod.streamInstructions().noneMatch(
- i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
- })
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 1", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 2", JumboStringMode.ALLOW)));
+ // TODO(b/138913138): not trivial; assigned only once in <init>
+ assertFalse(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 3", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 4", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 5", JumboStringMode.ALLOW)));
+ // TODO(b/138913138): not trivial; assigned multiple times, but within a certain
+ // range.
+ assertFalse(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 6", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 7", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod
+ .streamInstructions()
+ .noneMatch(i -> i.isConstString("Dead code: 8", JumboStringMode.ALLOW)));
+ })
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("The end");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index f28f242..564902a 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -40,7 +40,12 @@
TreeShaking18Test::unusedRemoved,
null,
null,
- ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"));
+ ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"),
+ options -> {
+ // TODO(b/125282093): Remove options modification once landed.
+ assert !options.enableValuePropagationForInstanceFields;
+ options.enableValuePropagationForInstanceFields = true;
+ });
}
private static void unusedRemoved(CodeInspector inspector) {
diff --git a/tools/archive.py b/tools/archive.py
index ff0f3db..c24babb 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -140,6 +140,7 @@
utils.D8,
utils.R8LIB,
utils.R8LIB_NO_DEPS,
+ utils.LIBRARY_DESUGAR_CONVERSIONS,
'-Pno_internal'
])
@@ -235,7 +236,9 @@
with utils.TempDir() as tmp_dir:
desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir, jar_name)
create_maven_release.generate_jar_with_desugar_configuration(
- utils.DESUGAR_CONFIGURATION, desugar_jdk_libs_configuration_jar)
+ utils.DESUGAR_CONFIGURATION,
+ utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
+ desugar_jdk_libs_configuration_jar)
if options.dry_run:
print('Dry run, not actually creating maven repo for '
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 72186a8..cc36693 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -16,6 +16,7 @@
from string import Template
import tempfile
import utils
+import zipfile
DEPENDENCYTEMPLATE = Template(
"""
@@ -333,16 +334,29 @@
out)
# Write the desugaring configuration of a jar file with the following content:
-# META-INF
-# desugar
-# d8
+# java/
+# util/
+# <java.util conversions classes>
+# time/
+# <java.time conversions classes>
+# META-INF/
+# desugar/
+# d8/
# desugar.json
-def generate_jar_with_desugar_configuration(configuration, destination):
+# lint/
+# <lint files>
+def generate_jar_with_desugar_configuration(configuration, conversions, destination):
with utils.TempDir() as tmp_dir:
+ # Add conversion classes.
+ with zipfile.ZipFile(conversions, 'r') as conversions_zip:
+ conversions_zip.extractall(tmp_dir)
+
+ # Add configuration
configuration_dir = join(tmp_dir, 'META-INF', 'desugar', 'd8')
makedirs(configuration_dir)
copyfile(configuration, join(configuration_dir, 'desugar.json'))
+ # Add lint configuartion.
lint_dir = join(configuration_dir, 'lint')
makedirs(lint_dir)
cmd = [
@@ -367,7 +381,9 @@
# Generate the jar with the configuration file.
jar_file = 'desugar_configuration.jar'
generate_jar_with_desugar_configuration(
- utils.DESUGAR_CONFIGURATION, jar_file)
+ utils.DESUGAR_CONFIGURATION,
+ utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
+ jar_file)
# Write the maven zip file.
generate_maven_zip(
'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
diff --git a/tools/r8_release.py b/tools/r8_release.py
index e661dc6..39f60c0 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -156,22 +156,33 @@
def release_studio_or_aosp(path, options, git_message):
with utils.ChangedWorkingDirectory(path):
- subprocess.call(['repo', 'abandon', 'update-r8'])
+ if not options.use_existing_work_branch:
+ subprocess.call(['repo', 'abandon', 'update-r8'])
if not options.no_sync:
subprocess.check_call(['repo', 'sync', '-cq', '-j', '16'])
prebuilts_r8 = os.path.join(path, 'prebuilts', 'r8')
- with utils.ChangedWorkingDirectory(prebuilts_r8):
- subprocess.check_call(['repo', 'start', 'update-r8'])
+ if not options.use_existing_work_branch:
+ with utils.ChangedWorkingDirectory(prebuilts_r8):
+ subprocess.check_call(['repo', 'start', 'update-r8'])
update_prebuilds(options.version, path)
with utils.ChangedWorkingDirectory(prebuilts_r8):
- subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
- process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
- stdin=subprocess.PIPE)
- return process.communicate(input='y\n')[0]
+ if not options.use_existing_work_branch:
+ subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
+ else:
+ print ('Not committing when --use-existing-work-branch. '
+ + 'Commit message should be:\n\n'
+ + git_message
+ + '\n')
+ # Don't upload if requested not to, or if changes are not committed due
+ # to --use-existing-work-branch
+ if not options.no_upload and not options.use_existing_work_branch:
+ process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
+ stdin=subprocess.PIPE)
+ return process.communicate(input='y\n')[0]
def prepare_aosp(args):
@@ -278,9 +289,10 @@
def prepare_google3(args):
assert args.version
# Check if an existing client exists.
- if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
- print "Remove the existing 'update-r8' client before continuing."
- sys.exit(1)
+ if not options.use_existing_work_branch:
+ if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
+ print "Remove the existing 'update-r8' client before continuing."
+ sys.exit(1)
def release_google3(options):
print "Releasing for Google 3"
@@ -354,7 +366,8 @@
assert options.version in blaze_result
- return g4_change(new_version, options.version)
+ if not options.no_upload:
+ return g4_change(new_version, options.version)
return release_google3
@@ -517,6 +530,14 @@
default=False,
action='store_true',
help='Release for google 3')
+ result.add_argument('--use-existing-work-branch', '--use_existing_work_branch',
+ default=False,
+ action='store_true',
+ help='Use existing work branch/CL in aosp/studio/google3')
+ result.add_argument('--no-upload', '--no_upload',
+ default=False,
+ action='store_true',
+ help="Don't upload for code review")
result.add_argument('--dry-run',
default=False,
action='store_true',
@@ -549,7 +570,7 @@
sys.exit(1)
targets_to_run.append(prepare_release(args))
- if args.google3 or args.studio:
+ if args.google3 or (args.studio and not args.no_sync):
utils.check_prodacces()
if args.google3:
diff --git a/tools/retrace.py b/tools/retrace.py
index 44120f2..6f3593f 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -24,6 +24,10 @@
help='Version to download r8lib map file for.',
default=None)
parser.add_argument(
+ '--tag',
+ help='Tag to download r8lib map file for.',
+ default=None)
+ parser.add_argument(
'--map',
help='Path to r8lib map.',
default=utils.R8LIB_JAR + '.map')
@@ -34,20 +38,36 @@
return parser.parse_args()
+def find_version_or_hash_from_tag(tag_or_hash):
+ info = subprocess.check_output([
+ 'git',
+ 'show',
+ tag_or_hash,
+ '-s',
+ '--format=oneline']).splitlines()[-1].split()
+ # The info should be on the following form [hash,"Version",version]
+ if len(info) == 3 and len(info[0]) == 40 and info[1] == "Version":
+ return info[2]
+ return None
+
+
def main():
args = parse_arguments()
r8lib_map_path = args.map
- hashOrVersion = args.commit_hash or args.version
- if hashOrVersion:
+ if args.tag:
+ hash_or_version = find_version_or_hash_from_tag(args.tag)
+ else:
+ hash_or_version = args.commit_hash or args.version
+ if hash_or_version:
download_path = archive.GetUploadDestination(
- hashOrVersion,
+ hash_or_version,
'r8lib.jar.map',
args.commit_hash is not None)
if utils.file_exists_on_cloud_storage(download_path):
r8lib_map_path = tempfile.NamedTemporaryFile().name
utils.download_file_from_cloud_storage(download_path, r8lib_map_path)
else:
- print('Could not find map file from argument: %s.' % hashOrVersion)
+ print('Could not find map file from argument: %s.' % hash_or_version)
return 1
retrace_args = [
diff --git a/tools/utils.py b/tools/utils.py
index fe0ca5e..51c5a12 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -41,6 +41,7 @@
R8LIB = 'r8lib'
R8LIB_NO_DEPS = 'r8LibNoDeps'
R8_SRC = 'sourceJar'
+LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
D8_JAR = os.path.join(LIBS, 'd8.jar')
R8_JAR = os.path.join(LIBS, 'r8.jar')
@@ -50,16 +51,10 @@
R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
-# TODO(b/134732760): The JSON configuration should be moved.
+LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(LIBS, 'library_desugar_conversions.zip')
+
DESUGAR_CONFIGURATION = os.path.join(
- TEST_ROOT,
- 'com',
- 'android',
- 'tools',
- 'r8',
- 'desugar',
- 'desugaredlibrary',
- 'desugar_jdk_libs.json')
+ 'src', 'library_desugar', 'desugar_jdk_libs.json')
DESUGAR_CONFIGURATION_MAVEN_ZIP = os.path.join(
LIBS, 'desugar_jdk_libs_configuration.zip')
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
@@ -561,10 +556,6 @@
configuration_json = json.loads(f.read())
configuration_format_version = \
configuration_json.get('configuration_format_version')
- if (configuration_format_version > 2):
- raise Exception(
- 'Unsupported "configuration_format_version" '
- + str(configuration_format_version))
version = configuration_json.get('version')
if not version:
raise Exception(