Merge commit '77627c90b270921a32af4e2d1c057e4f885e5928' into dev-release
diff --git a/.gitignore b/.gitignore
index e64324a..76604f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,10 @@
third_party/r8.tar.gz
third_party/r8mappings
third_party/r8mappings.tar.gz
+third_party/remapper
+third_party/remapper.tar.gz
+third_party/retrace_benchmark
+third_party/retrace_benchmark.tar.gz
third_party/rhino-1.7.10
third_party/rhino-1.7.10.tar.gz
third_party/rhino-android-1.1.1
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 8323666..80dcf13 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.origin.Origin;
@@ -14,10 +15,14 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -41,6 +46,8 @@
private final boolean optimizeMultidexForLinearAlloc;
private final BiPredicate<String, Long> dexClassChecksumFilter;
private final List<AssertionsConfiguration> assertionsConfiguration;
+ private final List<Consumer<Inspector>> outputInspections;
+ private int threadCount;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -54,6 +61,8 @@
optimizeMultidexForLinearAlloc = false;
dexClassChecksumFilter = (name, checksum) -> true;
assertionsConfiguration = new ArrayList<>();
+ outputInspections = null;
+ threadCount = ThreadUtils.NOT_SPECIFIED;
}
BaseCompilerCommand(
@@ -67,7 +76,9 @@
boolean optimizeMultidexForLinearAlloc,
boolean includeClassesChecksum,
BiPredicate<String, Long> dexClassChecksumFilter,
- List<AssertionsConfiguration> assertionsConfiguration) {
+ List<AssertionsConfiguration> assertionsConfiguration,
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -81,6 +92,8 @@
this.includeClassesChecksum = includeClassesChecksum;
this.dexClassChecksumFilter = dexClassChecksumFilter;
this.assertionsConfiguration = assertionsConfiguration;
+ this.outputInspections = outputInspections;
+ this.threadCount = threadCount;
}
/**
@@ -140,7 +153,16 @@
}
public List<AssertionsConfiguration> getAssertionsConfiguration() {
- return assertionsConfiguration;
+ return Collections.unmodifiableList(assertionsConfiguration);
+ }
+
+ public Collection<Consumer<Inspector>> getOutputInspections() {
+ return Collections.unmodifiableList(outputInspections);
+ }
+
+ /** Get the number of threads to use for the compilation. */
+ public int getThreadCount() {
+ return threadCount;
}
Reporter getReporter() {
@@ -166,6 +188,7 @@
private CompilationMode mode;
private int minApiLevel = 0;
+ private int threadCount = ThreadUtils.NOT_SPECIFIED;
protected DesugarState desugarState = DesugarState.ON;
private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>();
private boolean includeClassesChecksum = false;
@@ -173,6 +196,7 @@
private boolean optimizeMultidexForLinearAlloc = false;
private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
private List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
+ private List<Consumer<Inspector>> outputInspections = new ArrayList<>();
abstract CompilationMode defaultCompilationMode();
@@ -490,6 +514,20 @@
return self();
}
+ /** Set the number of threads to use for the compilation */
+ B setThreadCount(int threadCount) {
+ if (threadCount <= 0) {
+ getReporter().error("Invalid threadCount: " + threadCount);
+ } else {
+ this.threadCount = threadCount;
+ }
+ return self();
+ }
+
+ int getThreadCount() {
+ return threadCount;
+ }
+
/** Encodes the checksums into the dex output. */
public boolean getIncludeClassesChecksum() {
return includeClassesChecksum;
@@ -552,5 +590,31 @@
}
super.validate();
}
+
+ /**
+ * Add an inspection of the output program.
+ *
+ * <p>On a successful compilation the inspection is guaranteed to be called with inspectors that
+ * combined cover all of the output program. The inspections may be called multiple times with
+ * inspectors that have overlapping content (eg, classes synthesized based on multiple inputs
+ * can lead to this). Any overlapping content will be consistent, e.g., the inspection of type T
+ * will be the same (equality, not identify) as any other inspection of type T.
+ *
+ * <p>There is no guarantee of the order inspections are called or on which thread they are
+ * called.
+ *
+ * <p>The validity of an {@code Inspector} and all of its sub-inspectors, eg,
+ * {@MethodInspector}, is that of the callback. If any inspector object escapes the scope of the
+ * callback, the behavior of that inspector is undefined.
+ *
+ * @param inspection Inspection callback receiving inspectors denoting parts of the output.
+ */
+ public void addOutputInspection(Consumer<Inspector> inspection) {
+ outputInspections.add(inspection);
+ }
+
+ List<Consumer<Inspector>> getOutputInspections() {
+ return outputInspections;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index dbfe6b7..410441b 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -12,10 +12,14 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.function.Consumer;
public class BaseCompilerCommandParser<
C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>> {
+ protected static final String MIN_API_FLAG = "--min-api";
+ protected static final String THREAD_COUNT_FLAG = "--thread-count";
+
static final Iterable<String> ASSERTIONS_USAGE_MESSAGE =
Arrays.asList(
" --force-enable-assertions[:[<class name>|<package name>...]]",
@@ -32,19 +36,20 @@
" # is the default handling of javac assertion code when",
" # generating class file format.");
- void parseMinApi(B builder, String minApiString, Origin origin) {
- int minApi;
+ void parsePositiveIntArgument(
+ B builder, String flag, String argument, Origin origin, Consumer<Integer> setter) {
+ int value;
try {
- minApi = Integer.parseInt(minApiString);
+ value = Integer.parseInt(argument);
} catch (NumberFormatException e) {
- builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
- if (minApi < 1) {
- builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ if (value < 1) {
+ builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
- builder.setMinApiLevel(minApi);
+ setter.accept(value);
}
private static String PACKAGE_ASSERTION_POSTFIX = "...";
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 1fa0bb0..1ce0024 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -16,7 +16,9 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
+import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -220,6 +222,7 @@
markers.add(marker);
}
+ InspectorImpl.runInspections(options.outputInspections, app);
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(
app,
@@ -237,6 +240,7 @@
options,
marker == null ? null : ImmutableList.copyOf(markers),
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView),
null)
.write(executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index b7c9347..92f194a 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.inspector.Inspector;
+import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -15,10 +17,12 @@
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.function.BiPredicate;
+import java.util.function.Consumer;
/**
* Immutable command structure for an invocation of the {@link D8} compiler.
@@ -36,13 +40,6 @@
@Keep
public final class D8Command extends BaseCompilerCommand {
- private static class ClasspathInputOrigin extends InputFileOrigin {
-
- public ClasspathInputOrigin(Path file) {
- super("classpath input", file);
- }
- }
-
private static class DefaultD8DiagnosticsHandler implements DiagnosticsHandler {
@Override
@@ -229,6 +226,8 @@
desugaredLibraryKeepRuleConsumer,
libraryConfiguration,
getAssertionsConfiguration(),
+ getOutputInspections(),
+ getThreadCount(),
factory);
}
}
@@ -297,6 +296,8 @@
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount,
DexItemFactory factory) {
super(
inputApp,
@@ -309,7 +310,9 @@
optimizeMultidexForLinearAlloc,
encodeChecksum,
dexClassChecksumFilter,
- assertionsConfiguration);
+ assertionsConfiguration,
+ outputInspections,
+ threadCount);
this.intermediate = intermediate;
this.desugarGraphConsumer = desugarGraphConsumer;
this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -379,6 +382,11 @@
new AssertionConfigurationWithDefault(
AssertionTransformation.DISABLE, getAssertionsConfiguration());
+ internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
+
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index a6827d3..e9df325 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -29,10 +29,11 @@
"--output",
"--lib",
"--classpath",
- "--min-api",
+ MIN_API_FLAG,
"--main-dex-list",
"--main-dex-list-output",
- "--desugared-lib");
+ "--desugared-lib",
+ THREAD_COUNT_FLAG);
private static final String APK_EXTENSION = ".apk";
private static final String JAR_EXTENSION = ".jar";
@@ -121,7 +122,10 @@
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
" --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --intermediate # Compile an intermediate result intended for later",
@@ -243,13 +247,17 @@
builder.setMainDexListOutputPath(Paths.get(nextArg));
} else if (arg.equals("--optimize-multidex-for-linearalloc")) {
builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parseMinApi(builder, nextArg, origin);
+ parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.equals("--intermediate")) {
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 2509e12..94ea634 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -102,6 +103,7 @@
options,
markers,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null);
writer.write(executor);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 7ec9143..a9d4644 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
@@ -82,8 +83,6 @@
getDistribution(app, featureClassMapping, mapper);
for (Entry<String, LazyLoadedDexApplication.Builder> entry : applications.entrySet()) {
DexApplication featureApp = entry.getValue().build();
- // We use the same factory, reset sorting.
- featureApp.dexItemFactory.resetSortedIndices();
assert !options.hasMethodsFilter();
// Run d8 optimize to ensure jumbo strings are handled.
@@ -103,6 +102,7 @@
options,
markers,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null,
consumer)
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 9db1c33..c32fbb7 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
@@ -16,11 +17,13 @@
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
/** Immutable command structure for an invocation of the {@link L8} library compiler. */
@Keep
@@ -83,6 +86,8 @@
Reporter diagnosticsHandler,
DesugaredLibraryConfiguration libraryConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount,
DexItemFactory factory) {
super(
inputApp,
@@ -95,7 +100,9 @@
false,
false,
(name, checksum) -> true,
- assertionsConfiguration);
+ assertionsConfiguration,
+ outputInspections,
+ threadCount);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.libraryConfiguration = libraryConfiguration;
@@ -180,6 +187,9 @@
new AssertionConfigurationWithDefault(
AssertionTransformation.DISABLE, getAssertionsConfiguration());
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
@@ -317,6 +327,8 @@
getReporter(),
libraryConfiguration,
getAssertionsConfiguration(),
+ getOutputInspections(),
+ getThreadCount(),
factory);
}
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index bf35b67..976fb4a 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -19,7 +19,7 @@
public class L8CommandParser extends BaseCompilerCommandParser<L8Command, L8Command.Builder> {
private static final Set<String> OPTIONS_WITH_PARAMETER =
- ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
+ ImmutableSet.of("--output", "--lib", MIN_API_FLAG, "--desugared-lib", THREAD_COUNT_FLAG);
public static void main(String[] args) throws CompilationFailedException {
L8Command command = parse(args, Origin.root()).build();
@@ -43,7 +43,10 @@
" --output <file> # Output result in <outfile>.",
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
@@ -131,11 +134,12 @@
continue;
}
outputPath = Paths.get(nextArg);
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parseMinApi(builder, nextArg, origin);
+ parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
} else if (arg.equals("--lib")) {
@@ -144,6 +148,9 @@
builder.addProguardConfigurationFiles(Paths.get(nextArg));
} else if (arg.equals("--desugared-lib")) {
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.startsWith("--")) {
if (!tryParseAssertionArgument(builder, arg, origin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index d492e67..57f8395 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -91,6 +91,12 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ addType(clazz);
+ return false;
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexEncodedMethod target =
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8817968..5223520 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -28,8 +28,10 @@
import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis;
+import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.proto.GeneratedExtensionRegistryShrinker;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
@@ -46,9 +48,7 @@
import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.jar.CfApplicationWriter;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.kotlin.KotlinInfo;
-import com.android.tools.r8.kotlin.KotlinMemberInfo;
+import com.android.tools.r8.kotlin.KotlinInfoCollector;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.Minifier;
@@ -66,6 +66,7 @@
import com.android.tools.r8.shaking.AbstractMethodRemover;
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ClassInitFieldSynthesizer;
import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration;
import com.android.tools.r8.shaking.DiscardedChecker;
import com.android.tools.r8.shaking.Enqueuer;
@@ -91,7 +92,6 @@
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LineNumberOptimizer;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SelfRetraceTest;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -155,7 +155,6 @@
System.gc();
}
timing = Timing.create("R8", options);
- options.itemFactory.resetSortedIndices();
}
/**
@@ -197,10 +196,12 @@
DexApplication application,
AppView<?> appView,
GraphLense graphLense,
+ InitClassLens initClassLens,
NamingLens namingLens,
InternalOptions options,
ProguardMapSupplier proguardMapSupplier)
throws ExecutionException {
+ InspectorImpl.runInspections(options.outputInspections, application);
try {
Marker marker = options.getMarker(Tool.R8);
assert marker != null;
@@ -215,6 +216,7 @@
options,
Collections.singletonList(marker),
graphLense,
+ initClassLens,
namingLens,
proguardMapSupplier)
.write(executorService);
@@ -309,7 +311,8 @@
// Compute kotlin info before setting the roots and before
// kotlin metadata annotation is removed.
- computeKotlinInfoForProgramClasses(application, appView, executorService);
+ KotlinInfoCollector.computeKotlinInfoForProgramClasses(
+ application, appView, executorService);
// Add synthesized -assumenosideeffects from min api if relevant.
if (options.isGeneratingDex()) {
@@ -613,9 +616,7 @@
appView.setAppInfo(new AppInfoWithSubtyping(application));
- if (options.isShrinking()
- || options.isMinifying()
- || options.getProguardConfiguration().hasApplyMappingFile()) {
+ if (options.shouldRerunEnqueuer()) {
timing.begin("Post optimization code stripping");
try {
GraphConsumer keptGraphConsumer = null;
@@ -699,6 +700,9 @@
// Remove types that no longer exists from the computed main dex list.
mainDexClasses = mainDexClasses.prunedCopy(appView.appInfo().withLiveness());
}
+
+ // Synthesize fields for triggering class initializers.
+ new ClassInitFieldSynthesizer(appViewWithLiveness).run(executorService);
}
} finally {
timing.end();
@@ -819,6 +823,7 @@
application,
appView,
appView.graphLense(),
+ appView.initClassLens(),
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView, namingLens),
options,
proguardMapSupplier);
@@ -913,24 +918,6 @@
}
}
- private void computeKotlinInfoForProgramClasses(
- DexApplication application, AppView<?> appView, ExecutorService executorService)
- throws ExecutionException {
- if (appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations) {
- return;
- }
- Kotlin kotlin = appView.dexItemFactory().kotlin;
- Reporter reporter = options.reporter;
- ThreadUtils.processItems(
- application.classes(),
- programClass -> {
- KotlinInfo kotlinInfo = kotlin.getKotlinInfo(programClass, reporter);
- programClass.setKotlinInfo(kotlinInfo);
- KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo, reporter);
- },
- executorService);
- }
-
private static boolean verifyNoJarApplicationReaders(List<DexProgramClass> classes) {
for (DexProgramClass clazz : classes) {
for (DexEncodedMethod method : clazz.methods()) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 4051f9c..611bc7a 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.inspector.Inspector;
+import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
@@ -29,6 +31,7 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import java.io.InputStream;
import java.nio.file.Path;
@@ -567,7 +570,9 @@
desugaredLibraryKeepRuleConsumer,
libraryConfiguration,
featureSplitConfiguration,
- getAssertionsConfiguration());
+ getAssertionsConfiguration(),
+ getOutputInspections(),
+ getThreadCount());
return command;
}
@@ -724,7 +729,9 @@
StringConsumer desugaredLibraryKeepRuleConsumer,
DesugaredLibraryConfiguration libraryConfiguration,
FeatureSplitConfiguration featureSplitConfiguration,
- List<AssertionsConfiguration> assertionsConfiguration) {
+ List<AssertionsConfiguration> assertionsConfiguration,
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount) {
super(
inputApp,
mode,
@@ -736,7 +743,9 @@
optimizeMultidexForLinearAlloc,
encodeChecksum,
dexClassChecksumFilter,
- assertionsConfiguration);
+ assertionsConfiguration,
+ outputInspections,
+ threadCount);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -874,6 +883,8 @@
internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
+ internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
+
// Default is to remove all javac generated assertion code when generating dex.
assert internal.assertionsConfiguration == null;
internal.assertionsConfiguration =
@@ -903,6 +914,9 @@
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 22ebb5e..84aa83e 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -21,13 +21,14 @@
"--output",
"--lib",
"--classpath",
- "--min-api",
+ MIN_API_FLAG,
"--main-dex-rules",
"--main-dex-list",
"--main-dex-list-output",
"--pg-conf",
"--pg-map-output",
- "--desugared-lib");
+ "--desugared-lib",
+ THREAD_COUNT_FLAG);
public static void main(String[] args) throws CompilationFailedException {
R8Command command = parse(args, Origin.root()).build();
@@ -64,7 +65,10 @@
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
" --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
@@ -190,13 +194,18 @@
addLibraryArgument(builder, argsOrigin, nextArg);
} else if (arg.equals("--classpath")) {
builder.addClasspathFiles(Paths.get(nextArg));
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (state.hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", argsOrigin));
} else {
- parseMinApi(builder, nextArg, argsOrigin);
+ parsePositiveIntArgument(
+ builder, MIN_API_FLAG, nextArg, argsOrigin, builder::setMinApiLevel);
state.hasDefinedApiLevel = true;
}
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, argsOrigin, builder::setThreadCount);
} else if (arg.equals("--no-tree-shaking")) {
builder.setDisableTreeShaking(true);
} else if (arg.equals("--no-minification")) {
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 1d09319..0e86dc7 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -87,7 +88,6 @@
return null;
}
state.setPreviousResult(command.apply(app));
- app.options.itemFactory.resetSortedIndices();
}
}
@@ -186,7 +186,15 @@
StringConsumer proguardMapConsumer = options.proguardMapConsumer;
AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
ApplicationWriter writer =
- new ApplicationWriter(app, null, options, null, null, NamingLens.getIdentityLens(), null);
+ new ApplicationWriter(
+ app,
+ null,
+ options,
+ null,
+ null,
+ InitClassLens.getDefault(),
+ NamingLens.getIdentityLens(),
+ null);
writer.write(executor);
options.signalFinishedToConsumers();
compatSink.build().writeToDirectory(output, OutputMode.DexIndexed);
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
index 5500637..cd3fe0a 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -21,7 +21,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -324,9 +323,7 @@
private static List<DexProgramClass> getSortedClasses(DexApplication app) {
List<DexProgramClass> classes = new ArrayList<>(app.classes());
- app.dexItemFactory.sort(NamingLens.getIdentityLens());
- classes.sort(Comparator.comparing(DexProgramClass::getType));
- app.dexItemFactory.resetSortedIndices();
+ classes.sort((a, b) -> a.type.slowCompareTo(b.type, NamingLens.getIdentityLens()));
return classes;
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 9eedd04..ccd1686 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.cf.code.CfIinc;
+import com.android.tools.r8.cf.code.CfInitClass;
import com.android.tools.r8.cf.code.CfInstanceOf;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
@@ -321,6 +322,12 @@
appendType(constClass.getType());
}
+ public void print(CfInitClass initClass) {
+ indent();
+ builder.append("initclass ");
+ appendType(initClass.getClassValue());
+ }
+
public void print(CfReturnVoid ret) {
print("return");
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index fc2a080..e9997b0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -128,7 +129,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getAsmOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
index 53536d2..85c4773 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLength.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.CfState.Slot;
@@ -18,7 +19,7 @@
public class CfArrayLength extends CfInstruction {
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(Opcodes.ARRAYLENGTH);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
index 1f667b4..010bcfd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayLoad.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -55,7 +56,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getLoadType());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
index d0eadef..52df928 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArrayStore.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -53,7 +54,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getStoreType());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index e89c480..19ca32e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -29,7 +30,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitTypeInsn(Opcodes.CHECKCAST, lens.lookupInternalName(type));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
index dac3eca..259bfe3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.Cmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.NumericType;
@@ -78,7 +79,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getAsmOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 7f77ade..a203742 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -29,7 +30,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLdcInsn(Type.getObjectType(getInternalName(lens)));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index a9a1d73..04e569b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -29,7 +30,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLdcInsn(handle.toAsmHandle(lens));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index 063b6fa..a67c3ed 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -29,7 +30,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLdcInsn(Type.getType(type.toDescriptorString(lens)));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
index 7ad901a..c3be968 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNull.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -18,7 +19,7 @@
public class CfConstNull extends CfInstruction {
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(Opcodes.ACONST_NULL);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
index 72e4638..8977303 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstNumber.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -55,7 +56,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
switch (type) {
case INT:
{
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index d52d901..08ee865 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -41,7 +42,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLdcInsn(string.toString());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
index 36ba9b4..fbc4939 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -46,7 +47,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
throw new Unreachable(
"CfDexItemBasedConstString instructions should always be rewritten into CfConstString");
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index a000dcf..5812288 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -50,7 +51,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
String owner = lens.lookupInternalName(field.holder);
String name = lens.lookupName(declaringField).toString();
String desc = lens.lookupDescriptor(field.type).toString();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index e5a3c19..88d5ece 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -211,7 +212,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
int stackCount = computeStackCount();
Object[] stackTypes = computeStackTypes(stackCount, lens);
int localsCount = computeLocalsCount();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index efe67ba..412c97c7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -43,7 +44,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitJumpInsn(Opcodes.GOTO, target.getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index 1ba9c06..ebc6a98 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.ValueType;
@@ -68,7 +69,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitJumpInsn(getOpcode(), target.getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index 28a59d7..c2f34f7 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.If.Type;
import com.android.tools.r8.ir.code.ValueType;
@@ -68,7 +69,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitJumpInsn(getOpcode(), target.getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
index 92f2094..986d11f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIinc.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -25,7 +26,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitIincInsn(var, increment);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
new file mode 100644
index 0000000..a355f58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.CfSourceCode;
+import com.android.tools.r8.ir.conversion.CfState;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+
+public class CfInitClass extends CfInstruction {
+
+ private static final int OPCODE = org.objectweb.asm.Opcodes.GETSTATIC;
+
+ private final DexType clazz;
+
+ public CfInitClass(DexType clazz) {
+ this.clazz = clazz;
+ }
+
+ public DexType getClassValue() {
+ return clazz;
+ }
+
+ public int getOpcode() {
+ return OPCODE;
+ }
+
+ @Override
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
+ DexField field = initClassLens.getInitClassField(clazz);
+ String owner = lens.lookupInternalName(field.holder);
+ String name = lens.lookupName(field).toString();
+ String desc = lens.lookupDescriptor(field.type).toString();
+ visitor.visitFieldInsn(OPCODE, owner, name, desc);
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry, DexType context) {
+ registry.registerInitClass(clazz);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
+ int dest = state.push(builder.appView.dexItemFactory().intType).register;
+ builder.addInitClass(dest, clazz);
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, DexType context) {
+ return inliningConstraints.forInitClass(clazz, context);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index d4d2066..5d0997f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -38,7 +39,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitTypeInsn(Opcodes.INSTANCEOF, lens.lookupInternalName(type));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 43d209e..d3cc826 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -16,7 +17,7 @@
public abstract class CfInstruction {
- public abstract void write(MethodVisitor visitor, NamingLens lens);
+ public abstract void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens);
public abstract void print(CfPrinter printer);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 6311bfd..1a5dd5c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.GraphLenseLookupResult;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Invoke.Type;
@@ -69,7 +70,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
String owner = lens.lookupInternalName(method.holder);
String name = lens.lookupName(method).toString();
String desc = method.proto.toDescriptorString(lens);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 231c115..ef0ec1b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -41,7 +42,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
Object[] bsmArgs = new Object[bootstrapArgs.size()];
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
index 55aea68..7e678dc 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -28,7 +29,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
throw error();
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
index df9d6bd..3901a1a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLabel.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -41,7 +42,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLabel(getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
index ae1fd85..80d6a8e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLoad.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -55,7 +56,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitVarInsn(getLoadType(), var);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index 1d03017..c2775a0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -103,7 +104,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getAsmOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
index dc117dd..6386b3c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMonitor.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.Monitor.Type;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -29,7 +30,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(type == Type.ENTER ? Opcodes.MONITORENTER : Opcodes.MONITOREXIT);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 936f62d..d12d76b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -34,7 +35,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitMultiANewArrayInsn(lens.lookupInternalName(type), dimensions);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
index 254ee77..02b2a83 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -30,7 +31,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getAsmOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index 46821da..be087af 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -28,7 +29,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitTypeInsn(Opcodes.NEW, lens.lookupInternalName(type));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 10504d9..1ce6048 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -63,7 +64,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
if (type.isPrimitiveArrayType()) {
visitor.visitIntInsn(Opcodes.NEWARRAY, getPrimitiveTypeCode());
} else {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNop.java b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
index e124a0b..be9170c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNop.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -17,7 +18,7 @@
public class CfNop extends CfInstruction {
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(Opcodes.NOP);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
index c1118e5..dbedc8a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -40,7 +41,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(this.getAsmOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
index 035604b..2dac0c6 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfPosition.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -25,7 +26,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitLineNumber(position.line, label.getLabel());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index 76475fa..e1b6de1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -52,7 +53,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(getOpcode());
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index fd55aa0..0bbe1cd 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -22,7 +23,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(Opcodes.RETURN);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
index 5886058..5821c3f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStackInstruction.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -74,7 +75,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(opcode.opcode);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStore.java b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
index 5f8a05d..d560b82 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStore.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStore.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
@@ -55,7 +56,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitVarInsn(getStoreType(), var);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index 47aff14..bfbe30f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.CfState.Slot;
@@ -68,7 +69,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
Label[] labels = new Label[targets.size()];
for (int i = 0; i < targets.size(); i++) {
labels[i] = targets.get(i).getLabel();
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index a7609c0..c0c094d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.ir.conversion.CfSourceCode;
import com.android.tools.r8.ir.conversion.CfState;
import com.android.tools.r8.ir.conversion.CfState.Slot;
@@ -23,7 +24,7 @@
}
@Override
- public void write(MethodVisitor visitor, NamingLens lens) {
+ public void write(MethodVisitor visitor, InitClassLens initClassLens, NamingLens lens) {
visitor.visitInsn(Opcodes.ATHROW);
}
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java
new file mode 100644
index 0000000..9df8285
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.FieldMemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class DexInitClass extends Base2Format {
+
+ public static final int OPCODE = 0x60;
+ public static final String NAME = "InitClass";
+ public static final String SMALI_NAME = "initclass";
+
+ private final int dest;
+ private final DexType clazz;
+
+ public DexInitClass(int dest, DexType clazz) {
+ assert clazz.isClassType();
+ this.dest = dest;
+ this.clazz = clazz;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addInitClass(dest, clazz);
+ }
+
+ @Override
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
+ DexField field = indexedItems.getInitClassLens().getInitClassField(clazz);
+ field.collectIndexedItems(indexedItems, method, instructionOffset);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public int getOpcode() {
+ throw new Unreachable();
+ }
+
+ private int getOpcode(DexField field) {
+ FieldMemberType type = FieldMemberType.fromDexType(field.type);
+ switch (type) {
+ case INT:
+ case FLOAT:
+ return Sget.OPCODE;
+ case LONG:
+ case DOUBLE:
+ return SgetWide.OPCODE;
+ case OBJECT:
+ return SgetObject.OPCODE;
+ case BOOLEAN:
+ return SgetBoolean.OPCODE;
+ case BYTE:
+ return SgetByte.OPCODE;
+ case CHAR:
+ return SgetChar.OPCODE;
+ case SHORT:
+ return SgetShort.OPCODE;
+ default:
+ throw new Unreachable("Unexpected type: " + type);
+ }
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerInitClass(clazz);
+ }
+
+ @Override
+ public void write(ShortBuffer buffer, ObjectToOffsetMapping mapping) {
+ DexField field = mapping.getClinitField(clazz);
+ writeFirst(dest, buffer, getOpcode(field));
+ write16BitReference(field, buffer, mapping);
+ }
+
+ @Override
+ public int hashCode() {
+ return ((clazz.hashCode() << 8) | dest) ^ getClass().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ DexInitClass initClass = (DexInitClass) other;
+ return dest == initClass.dest && clazz == initClass.clazz;
+ }
+
+ @Override
+ public String toSmaliString(ClassNameMapper naming) {
+ return formatSmaliString("v" + dest + ", " + clazz.toSmaliString());
+ }
+
+ @Override
+ public String toString(ClassNameMapper naming) {
+ StringBuilder builder = new StringBuilder("v").append(dest).append(", ");
+ if (naming == null) {
+ builder.append(clazz.toSourceString());
+ } else {
+ builder.append(naming.originalNameOf(clazz));
+ }
+ return formatString(builder.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index d549e28..ea056cb 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -82,7 +82,11 @@
}
protected void writeFirst(int aa, ShortBuffer dest) {
- dest.put((short) (((aa & 0xff) << 8) | (getOpcode() & 0xff)));
+ writeFirst(aa, dest, getOpcode());
+ }
+
+ protected void writeFirst(int aa, ShortBuffer dest, int opcode) {
+ dest.put((short) (((aa & 0xff) << 8) | (opcode & 0xff)));
}
protected void writeFirst(int a, int b, ShortBuffer dest) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 32a4bf7..c4c318c 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -69,6 +70,7 @@
public final DexApplication application;
public final AppView<?> appView;
public final GraphLense graphLense;
+ public final InitClassLens initClassLens;
public final NamingLens namingLens;
public final InternalOptions options;
private final CodeToKeep desugaredLibraryCodeToKeep;
@@ -80,10 +82,16 @@
private static class SortAnnotations extends MixedSectionCollection {
+ private final NamingLens namingLens;
+
+ public SortAnnotations(NamingLens namingLens) {
+ this.namingLens = namingLens;
+ }
+
@Override
public boolean add(DexAnnotationSet dexAnnotationSet) {
// Annotation sets are sorted by annotation types.
- dexAnnotationSet.sort();
+ dexAnnotationSet.sort(namingLens);
return true;
}
@@ -141,6 +149,7 @@
InternalOptions options,
List<Marker> markers,
GraphLense graphLense,
+ InitClassLens initClassLens,
NamingLens namingLens,
ProguardMapSupplier proguardMapSupplier) {
this(
@@ -149,6 +158,7 @@
options,
markers,
graphLense,
+ initClassLens,
namingLens,
proguardMapSupplier,
null);
@@ -160,6 +170,7 @@
InternalOptions options,
List<Marker> markers,
GraphLense graphLense,
+ InitClassLens initClassLens,
NamingLens namingLens,
ProguardMapSupplier proguardMapSupplier,
DexIndexedConsumer consumer) {
@@ -171,6 +182,7 @@
this.desugaredLibraryCodeToKeep = CodeToKeep.createCodeToKeep(options, namingLens);
this.markers = markers;
this.graphLense = graphLense;
+ this.initClassLens = initClassLens;
this.namingLens = namingLens;
this.proguardMapSupplier = proguardMapSupplier;
this.programConsumer = consumer;
@@ -238,6 +250,7 @@
}
}
try {
+ // TODO(b/151313715): Move this to the writer threads.
insertAttributeAnnotations();
// Generate the dex file contents.
@@ -246,21 +259,12 @@
if (options.encodeChecksums) {
encodeChecksums(virtualFiles);
}
- // TODO(b/149190785): Only sort the live program!
- if (appView != null) {
- appView.appInfo().disableDefinitionForAssert();
- }
- namingLens.setIsSortingBeforeWriting(true);
- application.dexItemFactory.sort(namingLens);
- namingLens.setIsSortingBeforeWriting(false);
- if (appView != null) {
- appView.appInfo().enableDefinitionForAssert();
- }
assert markers == null
|| markers.isEmpty()
|| application.dexItemFactory.extractMarkers() != null;
- SortAnnotations sortAnnotations = new SortAnnotations();
+ // TODO(b/151313617): Sorting annotations mutates elements so run single threaded on main.
+ SortAnnotations sortAnnotations = new SortAnnotations(namingLens);
application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
for (VirtualFile virtualFile : virtualFiles) {
@@ -290,7 +294,8 @@
byteBufferProvider = options.getDexIndexedConsumer();
}
}
- ObjectToOffsetMapping objectMapping = virtualFile.computeMapping(application);
+ ObjectToOffsetMapping objectMapping =
+ virtualFile.computeMapping(application, namingLens, initClassLens);
MethodToCodeObjectMapping codeMapping =
rewriteCodeWithJumboStrings(
objectMapping, virtualFile.classes(), application);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 8b0cd00..a7c3ec0 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexDebugInfoForWriting;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedField;
@@ -41,7 +42,6 @@
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramClassVisitor;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -59,10 +59,12 @@
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -94,6 +96,7 @@
private final DexOutputBuffer dest;
private final MixedSectionOffsets mixedSectionOffsets;
private final CodeToKeep desugaredLibraryCodeToKeep;
+ private final Map<DexProgramClass, DexEncodedArray> staticFieldValues = new IdentityHashMap<>();
public FileWriter(
ByteBufferProvider provider,
@@ -118,10 +121,11 @@
if (Log.ENABLED) {
Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
}
+ List<DexAnnotationElement> elements = new ArrayList<>(Arrays.asList(annotation.elements));
+ elements.sort((a, b) -> a.name.slowCompareTo(b.name, mapping.getNamingLens()));
dest.putUleb128(mapping.getOffsetFor(annotation.type));
- dest.putUleb128(annotation.elements.length);
- assert PresortedComparable.isSorted(annotation.elements, (element) -> element.name);
- for (DexAnnotationElement element : annotation.elements) {
+ dest.putUleb128(elements.size());
+ for (DexAnnotationElement element : elements) {
dest.putUleb128(mapping.getOffsetFor(element.name));
element.value.writeTo(dest, mapping);
}
@@ -132,8 +136,6 @@
new ProgramClassDependencyCollector(application, mapping.getClasses())
.run(mapping.getClasses());
- // Ensure everything is sorted.
- assert mixedSectionOffsets.getClassesWithData().stream().allMatch(DexProgramClass::isSorted);
// Add the static values for all fields now that we have committed to their sorting.
mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
@@ -172,11 +174,22 @@
// Output the debug_info_items first, as they have no dependencies.
dest.moveTo(layout.getCodesOffset() + sizeOfCodeItems(codes));
- writeItems(mixedSectionOffsets.getDebugInfos(), layout::setDebugInfosOffset,
- this::writeDebugItem);
+ if (mixedSectionOffsets.getDebugInfos().isEmpty()) {
+ layout.setDebugInfosOffset(0);
+ } else {
+ // Ensure deterministic ordering of debug info by sorting consistent with the code objects.
+ layout.setDebugInfosOffset(dest.align(1));
+ Set<DexDebugInfo> seen = new HashSet<>(mixedSectionOffsets.getDebugInfos().size());
+ for (DexCode code : codes) {
+ DexDebugInfoForWriting info = code.getDebugInfoForWriting();
+ if (info != null && seen.add(info)) {
+ writeDebugItem(info);
+ }
+ }
+ }
// Remember the typelist offset for later.
- layout.setTypeListsOffset(dest.align(4)); // type_list are aligned.
+ layout.setTypeListsOffset(dest.align(4)); // type_list are aligned.
// Now output the code.
dest.moveTo(layout.getCodesOffset());
@@ -465,7 +478,7 @@
dest.putInt(mixedSectionOffsets.getOffsetForAnnotationsDirectory(clazz));
dest.putInt(
clazz.hasMethodsOrFields() ? mixedSectionOffsets.getOffsetFor(clazz) : Constants.NO_OFFSET);
- dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.getStaticValues()));
+ dest.putInt(mixedSectionOffsets.getOffsetFor(staticFieldValues.get(clazz)));
}
private void writeDebugItem(DexDebugInfo debugInfo) {
@@ -551,14 +564,14 @@
}
private void writeAnnotationSet(DexAnnotationSet set) {
- assert PresortedComparable.isSorted(set.annotations, (item) -> item.annotation.type)
- : "Unsorted annotation set: " + set.toSourceString();
mixedSectionOffsets.setOffsetFor(set, dest.align(4));
if (Log.ENABLED) {
Log.verbose(getClass(), "Writing AnnotationSet @ 0x%08x.", dest.position());
}
- dest.putInt(set.annotations.length);
- for (DexAnnotation annotation : set.annotations) {
+ List<DexAnnotation> annotations = new ArrayList<>(Arrays.asList(set.annotations));
+ annotations.sort((a, b) -> a.annotation.type.slowCompareTo(b.annotation.type, namingLens));
+ dest.putInt(annotations.size());
+ for (DexAnnotation annotation : annotations) {
dest.putInt(mixedSectionOffsets.getOffsetFor(annotation));
}
}
@@ -588,9 +601,11 @@
private void writeAnnotationDirectory(DexAnnotationDirectory annotationDirectory) {
mixedSectionOffsets.setOffsetForAnnotationsDirectory(annotationDirectory, dest.align(4));
dest.putInt(mixedSectionOffsets.getOffsetFor(annotationDirectory.getClazzAnnotations()));
- List<DexEncodedMethod> methodAnnotations = annotationDirectory.getMethodAnnotations();
- List<DexEncodedMethod> parameterAnnotations = annotationDirectory.getParameterAnnotations();
- List<DexEncodedField> fieldAnnotations = annotationDirectory.getFieldAnnotations();
+ List<DexEncodedMethod> methodAnnotations =
+ annotationDirectory.sortMethodAnnotations(namingLens);
+ List<DexEncodedMethod> parameterAnnotations =
+ annotationDirectory.sortParameterAnnotations(namingLens);
+ List<DexEncodedField> fieldAnnotations = annotationDirectory.sortFieldAnnotations(namingLens);
dest.putInt(fieldAnnotations.size());
dest.putInt(methodAnnotations.size());
dest.putInt(parameterAnnotations.size());
@@ -602,10 +617,12 @@
item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotationsList));
}
- private void writeEncodedFields(List<DexEncodedField> fields) {
- assert PresortedComparable.isSorted(fields);
+ private void writeEncodedFields(List<DexEncodedField> unsortedFields) {
+ List<DexEncodedField> fields = new ArrayList<>(unsortedFields);
+ fields.sort((a, b) -> a.field.slowCompareTo(b.field, namingLens));
int currentOffset = 0;
for (DexEncodedField field : fields) {
+ assert field.validateDexValue(application.dexItemFactory);
int nextOffset = mapping.getOffsetFor(field.field);
assert nextOffset - currentOffset >= 0;
dest.putUleb128(nextOffset - currentOffset);
@@ -615,8 +632,10 @@
}
}
- private void writeEncodedMethods(List<DexEncodedMethod> methods, boolean isSharedSynthetic) {
- assert PresortedComparable.isSorted(methods);
+ private void writeEncodedMethods(
+ List<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
+ List<DexEncodedMethod> methods = new ArrayList<>(unsortedMethods);
+ methods.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
int currentOffset = 0;
for (DexEncodedMethod method : methods) {
int nextOffset = mapping.getOffsetFor(method.method);
@@ -657,12 +676,12 @@
}
private void addStaticFieldValues(DexProgramClass clazz) {
- clazz.computeStaticValues();
// We have collected the individual components of this array due to the data stored in
// DexEncodedField#staticValues. However, we have to collect the DexEncodedArray itself
// here.
- DexEncodedArray staticValues = clazz.getStaticValues();
+ DexEncodedArray staticValues = clazz.computeStaticValuesArray(namingLens);
if (staticValues != null) {
+ staticFieldValues.put(clazz, staticValues);
mixedSectionOffsets.add(staticValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
index 4da7302..45b8bfe 100644
--- a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.InitClassLens;
/**
* Common interface for constant pools.
@@ -99,6 +100,10 @@
*/
boolean addMethodHandle(DexMethodHandle methodHandle);
+ default InitClassLens getInitClassLens() {
+ return InitClassLens.getDefault();
+ }
+
default DexString getRenamedName(DexMethod method) {
return method.name;
}
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 37649d3..e623f5e 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Maps;
@@ -69,7 +70,7 @@
public void updateNumbersOfIds() {
// Use a temporary VirtualFile to evaluate the number of ids in the group.
- VirtualFile virtualFile = new VirtualFile(0, namingLens);
+ VirtualFile virtualFile = new VirtualFile(0, initClassLens, namingLens);
// Note: sort not needed.
for (DexProgramClass clazz : members) {
virtualFile.addClass(clazz);
@@ -283,6 +284,7 @@
private final Set<DexProgramClass> classes;
private final DexApplication app;
private int dexIndexOffset;
+ private final InitClassLens initClassLens;
private final NamingLens namingLens;
private final DirectSubClassesInfo directSubClasses;
@@ -290,8 +292,8 @@
VirtualFile mainDex,
List<VirtualFile> dexes,
Set<DexProgramClass> classes,
- Map<DexProgramClass, String> originalNames,
int dexIndexOffset,
+ InitClassLens initClassLens,
NamingLens namingLens,
DexApplication app,
ExecutorService executorService) {
@@ -299,6 +301,7 @@
this.dexes = dexes;
this.classes = classes;
this.dexIndexOffset = dexIndexOffset;
+ this.initClassLens = initClassLens;
this.namingLens = namingLens;
this.app = app;
this.executorService = executorService;
@@ -370,7 +373,8 @@
}
private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> dexBlackList) {
- VirtualFileCycler cycler = new VirtualFileCycler(dexes, namingLens, dexIndexOffset);
+ VirtualFileCycler cycler =
+ new VirtualFileCycler(dexes, initClassLens, namingLens, dexIndexOffset);
if (group.members.isEmpty()) {
return Collections.emptyList();
} else if (group.canFitInOneDex()) {
@@ -418,7 +422,8 @@
Collections.sort(layers);
Collection<VirtualFile> usedDex = new ArrayList<>();
- VirtualFileCycler cycler = new VirtualFileCycler(dexes, namingLens, dexIndexOffset);
+ VirtualFileCycler cycler =
+ new VirtualFileCycler(dexes, initClassLens, namingLens, dexIndexOffset);
// Don't modify input dexBlackList. Think about modifying the input collection considering this
// is private API.
Set<VirtualFile> currentBlackList = new HashSet<>(dexBlackList);
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index 2936d80..e05af85 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -284,7 +284,7 @@
instruction.setOffset(orignalOffset + offsetDelta);
if (instruction instanceof ConstString) {
ConstString string = (ConstString) instruction;
- if (string.getString().compareTo(firstJumboString) >= 0) {
+ if (string.getString().slowCompareTo(firstJumboString) >= 0) {
ConstStringJumbo jumboString = new ConstStringJumbo(string.AA, string.getString());
jumboString.setOffset(string.getOffset());
offsetDelta++;
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 02a6129..a9ff477 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -75,23 +76,29 @@
private final DexProgramClass primaryClass;
- VirtualFile(int id, NamingLens namingLens) {
- this(id, namingLens, null, null);
+ VirtualFile(int id, InitClassLens initClassLens, NamingLens namingLens) {
+ this(id, initClassLens, namingLens, null, null);
}
- VirtualFile(int id, NamingLens namingLens, FeatureSplit featureSplit) {
- this(id, namingLens, null, featureSplit);
- }
-
- private VirtualFile(int id, NamingLens namingLens, DexProgramClass primaryClass) {
- this(id, namingLens, primaryClass, null);
+ VirtualFile(
+ int id, InitClassLens initClassLens, NamingLens namingLens, FeatureSplit featureSplit) {
+ this(id, initClassLens, namingLens, null, featureSplit);
}
private VirtualFile(
- int id, NamingLens namingLens, DexProgramClass primaryClass, FeatureSplit featureSplit) {
+ int id, InitClassLens initClassLens, NamingLens namingLens, DexProgramClass primaryClass) {
+ this(id, initClassLens, namingLens, primaryClass, null);
+ }
+
+ private VirtualFile(
+ int id,
+ InitClassLens initClassLens,
+ NamingLens namingLens,
+ DexProgramClass primaryClass,
+ FeatureSplit featureSplit) {
this.id = id;
- this.indexedItems = new VirtualFileIndexedItemCollection(namingLens);
- this.transaction = new IndexedItemTransaction(indexedItems, namingLens);
+ this.indexedItems = new VirtualFileIndexedItemCollection(initClassLens, namingLens);
+ this.transaction = new IndexedItemTransaction(indexedItems, initClassLens, namingLens);
this.primaryClass = primaryClass;
this.featureSplit = featureSplit;
}
@@ -174,10 +181,13 @@
return prefix;
}
- public ObjectToOffsetMapping computeMapping(DexApplication application) {
+ public ObjectToOffsetMapping computeMapping(
+ DexApplication application, NamingLens namingLens, InitClassLens initClassLens) {
assert transaction.isEmpty();
return new ObjectToOffsetMapping(
application,
+ namingLens,
+ initClassLens,
indexedItems.classes,
indexedItems.protos,
indexedItems.types,
@@ -273,7 +283,8 @@
// Assign dedicated virtual files for all program classes.
for (DexProgramClass clazz : application.classes()) {
if (!combineSyntheticClassesWithPrimaryClass || clazz.getSynthesizedFrom().isEmpty()) {
- VirtualFile file = new VirtualFile(virtualFiles.size(), writer.namingLens, clazz);
+ VirtualFile file =
+ new VirtualFile(virtualFiles.size(), writer.initClassLens, writer.namingLens, clazz);
virtualFiles.add(file);
file.addClass(clazz);
files.put(clazz, file);
@@ -306,7 +317,7 @@
this.options = options;
// Create the primary dex file. The distribution will add more if needed.
- mainDexFile = new VirtualFile(0, writer.namingLens);
+ mainDexFile = new VirtualFile(0, writer.initClassLens, writer.namingLens);
assert virtualFiles.isEmpty();
virtualFiles.add(mainDexFile);
addMarkers(mainDexFile);
@@ -423,7 +434,8 @@
featureSplitClasses.entrySet()) {
// Add a new virtual file, start from index 0 again
VirtualFile featureFile =
- new VirtualFile(0, writer.namingLens, featureSplitSetEntry.getKey());
+ new VirtualFile(
+ 0, writer.initClassLens, writer.namingLens, featureSplitSetEntry.getKey());
virtualFiles.add(featureFile);
addMarkers(featureFile);
Set<DexProgramClass> featureClasses =
@@ -437,6 +449,7 @@
application.dexItemFactory,
fillStrategy,
0,
+ writer.initClassLens,
writer.namingLens)
.call();
}
@@ -471,7 +484,7 @@
assert !virtualFiles.get(0).isEmpty();
assert virtualFiles.size() == 1;
// The main dex file is filtered out, so ensure at least one file for the remaining classes.
- virtualFiles.add(new VirtualFile(1, writer.namingLens));
+ virtualFiles.add(new VirtualFile(1, writer.initClassLens, writer.namingLens));
filesForDistribution = virtualFiles.subList(1, virtualFiles.size());
fileIndexOffset = 1;
}
@@ -480,16 +493,29 @@
removeFeatureSplitClassesGetMapping();
if (multidexLegacy && options.enableInheritanceClassInDexDistributor) {
- new InheritanceClassInDexDistributor(mainDexFile, filesForDistribution, classes,
- originalNames, fileIndexOffset, writer.namingLens, writer.application, executorService)
+ new InheritanceClassInDexDistributor(
+ mainDexFile,
+ filesForDistribution,
+ classes,
+ fileIndexOffset,
+ writer.initClassLens,
+ writer.namingLens,
+ writer.application,
+ executorService)
.distribute();
} else {
// Sort the remaining classes based on the original names.
// This with make classes from the same package be adjacent.
classes = sortClassesByPackage(classes, originalNames);
new PackageSplitPopulator(
- filesForDistribution, classes, originalNames, application.dexItemFactory,
- fillStrategy, fileIndexOffset, writer.namingLens)
+ filesForDistribution,
+ classes,
+ originalNames,
+ application.dexItemFactory,
+ fillStrategy,
+ fileIndexOffset,
+ writer.initClassLens,
+ writer.namingLens)
.call();
}
addFeatureSplitFiles(featureSplitClasses, fillStrategy);
@@ -526,6 +552,7 @@
private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
+ private final InitClassLens initClassLens;
private final NamingLens namingLens;
private final Set<DexProgramClass> classes = Sets.newIdentityHashSet();
@@ -537,10 +564,9 @@
private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
private final Set<DexMethodHandle> methodHandles = Sets.newIdentityHashSet();
- public VirtualFileIndexedItemCollection(
- NamingLens namingLens) {
+ public VirtualFileIndexedItemCollection(InitClassLens initClassLens, NamingLens namingLens) {
+ this.initClassLens = initClassLens;
this.namingLens = namingLens;
-
}
@Override
@@ -596,6 +622,11 @@
}
@Override
+ public InitClassLens getInitClassLens() {
+ return initClassLens;
+ }
+
+ @Override
public DexString getRenamedDescriptor(DexType type) {
return namingLens.lookupDescriptor(type);
}
@@ -615,6 +646,7 @@
public static class IndexedItemTransaction implements IndexedItemCollection {
private final VirtualFileIndexedItemCollection base;
+ private final InitClassLens initClassLens;
private final NamingLens namingLens;
private final Set<DexProgramClass> classes = new LinkedHashSet<>();
@@ -626,9 +658,10 @@
private final Set<DexCallSite> callSites = new LinkedHashSet<>();
private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
- private IndexedItemTransaction(VirtualFileIndexedItemCollection base,
- NamingLens namingLens) {
+ private IndexedItemTransaction(
+ VirtualFileIndexedItemCollection base, InitClassLens initClassLens, NamingLens namingLens) {
this.base = base;
+ this.initClassLens = initClassLens;
this.namingLens = namingLens;
}
@@ -685,6 +718,11 @@
}
@Override
+ public InitClassLens getInitClassLens() {
+ return initClassLens;
+ }
+
+ @Override
public DexString getRenamedDescriptor(DexType type) {
return namingLens.lookupDescriptor(type);
}
@@ -760,6 +798,7 @@
static class VirtualFileCycler {
private final List<VirtualFile> files;
+ private final InitClassLens initClassLens;
private final NamingLens namingLens;
private int nextFileId;
@@ -767,8 +806,13 @@
private Iterator<VirtualFile> activeFiles;
private FeatureSplit featuresplit;
- VirtualFileCycler(List<VirtualFile> files, NamingLens namingLens, int fileIndexOffset) {
+ VirtualFileCycler(
+ List<VirtualFile> files,
+ InitClassLens initClassLens,
+ NamingLens namingLens,
+ int fileIndexOffset) {
this.files = files;
+ this.initClassLens = initClassLens;
this.namingLens = namingLens;
nextFileId = files.size() + fileIndexOffset;
@@ -799,7 +843,8 @@
if (hasNext()) {
return activeFiles.next();
} else {
- VirtualFile newFile = new VirtualFile(nextFileId++, namingLens, featuresplit);
+ VirtualFile newFile =
+ new VirtualFile(nextFileId++, initClassLens, namingLens, featuresplit);
files.add(newFile);
allFilesCyclic = Iterators.cycle(files);
return newFile;
@@ -830,7 +875,7 @@
}
VirtualFile addFile() {
- VirtualFile newFile = new VirtualFile(nextFileId++, namingLens, featuresplit);
+ VirtualFile newFile = new VirtualFile(nextFileId++, initClassLens, namingLens, featuresplit);
files.add(newFile);
reset();
@@ -875,12 +920,13 @@
DexItemFactory dexItemFactory,
FillStrategy fillStrategy,
int fileIndexOffset,
+ InitClassLens initClassLens,
NamingLens namingLens) {
this.classes = new ArrayList<>(classes);
this.originalNames = originalNames;
this.dexItemFactory = dexItemFactory;
this.fillStrategy = fillStrategy;
- this.cycler = new VirtualFileCycler(files, namingLens, fileIndexOffset);
+ this.cycler = new VirtualFileCycler(files, initClassLens, namingLens, fileIndexOffset);
}
static boolean coveredByPrefix(String originalName, String currentPrefix) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 640b7a1..2ddba3f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -705,10 +705,4 @@
entry.getKey(),
entry.getValue());
}
-
- // TODO(b/149190785): Remove once fixed.
- public void enableDefinitionForAssert() {}
-
- // TODO(b/149190785): Remove once fixed.
- public void disableDefinitionForAssert() {}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index d1e70ed..5f2b826 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -381,7 +381,7 @@
return false;
}
assert potentialHolder.isInterface();
- for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods) {
+ for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods()) {
if (virtualMethod.method.hasSameProtoAndName(method.method)
&& virtualMethod.accessFlags.isSameVisibility(method.accessFlags)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9ed88a6..e504e54 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -45,6 +45,7 @@
private final DexItemFactory dexItemFactory;
private final WholeProgramOptimizations wholeProgramOptimizations;
private GraphLense graphLense;
+ private InitClassLens initClassLens;
private final InternalOptions options;
private RootSet rootSet;
private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
@@ -90,6 +91,7 @@
this.dexItemFactory = appInfo != null ? appInfo.dexItemFactory() : null;
this.wholeProgramOptimizations = wholeProgramOptimizations;
this.graphLense = GraphLense.getIdentityLense();
+ this.initClassLens = InitClassLens.getDefault();
this.options = options;
this.rewritePrefix = mapper;
@@ -310,6 +312,18 @@
return false;
}
+ public boolean canUseInitClass() {
+ return options.shouldRerunEnqueuer() && !initClassLens.isFinal();
+ }
+
+ public InitClassLens initClassLens() {
+ return initClassLens;
+ }
+
+ public void setInitClassLens(InitClassLens initClassLens) {
+ this.initClassLens = initClassLens;
+ }
+
public void setInitializedClassesInInstanceMethods(
InitializedClassesInInstanceMethods initializedClassesInInstanceMethods) {
this.initializedClassesInInstanceMethods = initializedClassesInInstanceMethods;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 4f8fa23..0c4267c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -202,11 +202,12 @@
NamingLens namingLens,
AppView<?> appView,
int classFileVersion) {
+ InitClassLens initClassLens = appView.initClassLens();
InternalOptions options = appView.options();
CfLabel parameterLabel = null;
if (shouldAddParameterNames(method, appView)) {
parameterLabel = new CfLabel();
- parameterLabel.write(visitor, namingLens);
+ parameterLabel.write(visitor, initClassLens, namingLens);
}
for (CfInstruction instruction : instructions) {
if (instruction instanceof CfFrame
@@ -214,7 +215,7 @@
|| (classFileVersion == 50 && !options.shouldKeepStackMapTable()))) {
continue;
}
- instruction.write(visitor, namingLens);
+ instruction.write(visitor, initClassLens, namingLens);
}
visitor.visitEnd();
visitor.visitMaxs(maxStack, maxLocals);
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java b/src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java
new file mode 100644
index 0000000..56a1733
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInitClassLens.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public class DefaultInitClassLens extends InitClassLens {
+
+ private static final DefaultInitClassLens INSTANCE = new DefaultInitClassLens();
+
+ private DefaultInitClassLens() {}
+
+ public static DefaultInitClassLens getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public DexField getInitClassField(DexType type) {
+ throw new Unreachable("Unexpected InitClass instruction for `" + type.toSourceString() + "`");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java b/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
index bb209b5..7d6b095 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultUseRegistry.java
@@ -11,6 +11,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return true;
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 2d0667b..a6b7824 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -6,10 +6,9 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.utils.OrderedMergingIterator;
+import com.android.tools.r8.naming.NamingLens;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Function;
public class DexAnnotationDirectory extends DexItem {
@@ -22,28 +21,21 @@
public DexAnnotationDirectory(DexProgramClass clazz) {
this.clazz = clazz;
this.classHasOnlyInternalizableAnnotations = clazz.hasOnlyInternalizableAnnotations();
- assert isSorted(clazz.directMethods());
- assert isSorted(clazz.virtualMethods());
- OrderedMergingIterator<DexEncodedMethod, DexMethod> methods =
- new OrderedMergingIterator<>(clazz.directMethods(), clazz.virtualMethods());
methodAnnotations = new ArrayList<>();
parameterAnnotations = new ArrayList<>();
- while (methods.hasNext()) {
- DexEncodedMethod method = methods.next();
- if (!method.annotations().isEmpty()) {
- methodAnnotations.add(method);
- }
- if (!method.parameterAnnotationsList.isEmpty()) {
- parameterAnnotations.add(method);
- }
- }
- assert isSorted(clazz.staticFields());
- assert isSorted(clazz.instanceFields());
- OrderedMergingIterator<DexEncodedField, DexField> fields =
- new OrderedMergingIterator<>(clazz.staticFields(), clazz.instanceFields());
fieldAnnotations = new ArrayList<>();
- while (fields.hasNext()) {
- DexEncodedField field = fields.next();
+ clazz
+ .getMethodCollection()
+ .forEachMethod(
+ method -> {
+ if (!method.annotations().isEmpty()) {
+ methodAnnotations.add(method);
+ }
+ if (!method.parameterAnnotationsList.isEmpty()) {
+ parameterAnnotations.add(method);
+ }
+ });
+ for (DexEncodedField field : clazz.fields()) {
if (!field.annotations().isEmpty()) {
fieldAnnotations.add(field);
}
@@ -54,15 +46,18 @@
return clazz.annotations();
}
- public List<DexEncodedMethod> getMethodAnnotations() {
+ public List<DexEncodedMethod> sortMethodAnnotations(NamingLens namingLens) {
+ methodAnnotations.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
return methodAnnotations;
}
- public List<DexEncodedMethod> getParameterAnnotations() {
+ public List<DexEncodedMethod> sortParameterAnnotations(NamingLens namingLens) {
+ parameterAnnotations.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
return parameterAnnotations;
}
- public List<DexEncodedField> getFieldAnnotations() {
+ public List<DexEncodedField> sortFieldAnnotations(NamingLens namingLens) {
+ fieldAnnotations.sort((a, b) -> a.field.slowCompareTo(b.field, namingLens));
return fieldAnnotations;
}
@@ -106,22 +101,4 @@
public void collectMixedSectionItems(MixedSectionCollection collection) {
throw new Unreachable();
}
-
- private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> boolean isSorted(
- List<D> items) {
- return isSorted(items, DexEncodedMember::toReference);
- }
-
- private static <S, T extends Comparable<T>> boolean isSorted(
- List<S> items, Function<S, T> getter) {
- T current = null;
- for (S item : items) {
- T next = getter.apply(item);
- if (current != null && current.compareTo(next) >= 0) {
- return false;
- }
- current = next;
- }
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index dd24ed6..7371902 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -5,10 +5,10 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
import com.google.common.collect.Sets;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -93,12 +93,13 @@
return annotations.length == 0;
}
- public void sort() {
+ public void sort(NamingLens namingLens) {
if (sorted != UNSORTED) {
assert sorted == sortedHashCode();
return;
}
- Arrays.sort(annotations, Comparator.comparing(a -> a.annotation.type));
+ Arrays.sort(
+ annotations, (a, b) -> a.annotation.type.slowCompareTo(b.annotation.type, namingLens));
for (DexAnnotation annotation : annotations) {
annotation.annotation.sort();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a75fd4e..773a71a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.PredicateUtils;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -22,7 +21,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -33,12 +31,6 @@
void setField(int index, DexEncodedField field);
}
- public interface MethodSetter {
- void setMethod(int index, DexEncodedMethod method);
- }
-
- private Optional<DexEncodedMethod> cachedClassInitializer = null;
-
public final Origin origin;
public DexType type;
public final ClassAccessFlags accessFlags;
@@ -55,10 +47,7 @@
protected DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
/** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
-
- /** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ protected final MethodCollection methodCollection;
/** Enclosing context of this class if it is an inner class, null otherwise. */
private EnclosingMethodAttribute enclosingMethod;
@@ -96,6 +85,7 @@
this.type = type;
setStaticFields(staticFields);
setInstanceFields(instanceFields);
+ this.methodCollection = new MethodCollection(this);
setDirectMethods(directMethods);
setVirtualMethods(virtualMethods);
this.nestHost = nestHost;
@@ -133,12 +123,16 @@
return Iterables.concat(fields(), methods());
}
- public Iterable<DexEncodedMethod> methods() {
- return methods(Predicates.alwaysTrue());
+ public MethodCollection getMethodCollection() {
+ return methodCollection;
}
- public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
- return Iterables.concat(directMethods(predicate), virtualMethods(predicate));
+ public Iterable<DexEncodedMethod> methods() {
+ return methodCollection.methods();
+ }
+
+ public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+ return methodCollection.methods(predicate);
}
@Override
@@ -147,145 +141,47 @@
}
public List<DexEncodedMethod> directMethods() {
- assert directMethods != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(directMethods));
- }
- return Arrays.asList(directMethods);
+ return methodCollection.directMethods();
}
public Iterable<DexEncodedMethod> directMethods(Predicate<? super DexEncodedMethod> predicate) {
- return Iterables.filter(Arrays.asList(directMethods), predicate::test);
+ return Iterables.filter(directMethods(), predicate::test);
}
public void appendDirectMethod(DexEncodedMethod method) {
- cachedClassInitializer = null;
- DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + 1];
- System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
- newMethods[directMethods.length] = method;
- directMethods = newMethods;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendDirectMethod(method);
}
public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
- cachedClassInitializer = null;
- DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + methods.size()];
- System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
- int i = directMethods.length;
- for (DexEncodedMethod method : methods) {
- newMethods[i] = method;
- i++;
- }
- directMethods = newMethods;
- assert verifyCorrectnessOfMethodHolders(methods);
- assert verifyNoDuplicateMethods();
- }
-
- public void removeDirectMethod(int index) {
- cachedClassInitializer = null;
- DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length - 1];
- System.arraycopy(directMethods, 0, newMethods, 0, index);
- System.arraycopy(directMethods, index + 1, newMethods, index, directMethods.length - index - 1);
- directMethods = newMethods;
+ methodCollection.appendDirectMethods(methods);
}
public void removeDirectMethod(DexMethod method) {
- int index = -1;
- for (int i = 0; i < directMethods.length; i++) {
- if (method.match(directMethods[i])) {
- index = i;
- break;
- }
- }
- if (index >= 0) {
- removeDirectMethod(index);
- }
- }
-
- public void setDirectMethod(int index, DexEncodedMethod method) {
- cachedClassInitializer = null;
- directMethods[index] = method;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.removeDirectMethod(method);
}
public void setDirectMethods(DexEncodedMethod[] methods) {
- cachedClassInitializer = null;
- directMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
- assert verifyCorrectnessOfMethodHolders(directMethods());
- assert verifyNoDuplicateMethods();
+ methodCollection.setDirectMethods(methods);
}
public List<DexEncodedMethod> virtualMethods() {
- assert virtualMethods != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(virtualMethods));
- }
- return Arrays.asList(virtualMethods);
+ return methodCollection.virtualMethods();
}
public Iterable<DexEncodedMethod> virtualMethods(Predicate<? super DexEncodedMethod> predicate) {
- return Iterables.filter(Arrays.asList(virtualMethods), predicate::test);
+ return Iterables.filter(virtualMethods(), predicate::test);
}
public void appendVirtualMethod(DexEncodedMethod method) {
- DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1];
- System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
- newMethods[virtualMethods.length] = method;
- virtualMethods = newMethods;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendVirtualMethod(method);
}
public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
- DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + methods.size()];
- System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
- int i = virtualMethods.length;
- for (DexEncodedMethod method : methods) {
- newMethods[i] = method;
- i++;
- }
- virtualMethods = newMethods;
- assert verifyCorrectnessOfMethodHolders(methods);
- assert verifyNoDuplicateMethods();
- }
-
- public void removeVirtualMethod(int index) {
- DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length - 1];
- System.arraycopy(virtualMethods, 0, newMethods, 0, index);
- System.arraycopy(
- virtualMethods, index + 1, newMethods, index, virtualMethods.length - index - 1);
- virtualMethods = newMethods;
- }
-
- public void setVirtualMethod(int index, DexEncodedMethod method) {
- virtualMethods[index] = method;
- assert verifyCorrectnessOfMethodHolder(method);
- assert verifyNoDuplicateMethods();
+ methodCollection.appendVirtualMethods(methods);
}
public void setVirtualMethods(DexEncodedMethod[] methods) {
- virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
- assert verifyCorrectnessOfMethodHolders(virtualMethods());
- assert verifyNoDuplicateMethods();
- }
-
- private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
- assert method.method.holder == type
- : "Expected method `"
- + method.method.toSourceString()
- + "` to have holder `"
- + type.toSourceString()
- + "`";
- return true;
- }
-
- private boolean verifyCorrectnessOfMethodHolders(Iterable<DexEncodedMethod> methods) {
- for (DexEncodedMethod method : methods) {
- assert verifyCorrectnessOfMethodHolder(method);
- }
- return true;
+ methodCollection.setVirtualMethods(methods);
}
private boolean verifyNoAbstractMethodsOnNonAbstractClasses(
@@ -301,75 +197,16 @@
return true;
}
- private boolean verifyNoDuplicateMethods() {
- Set<DexMethod> unique = Sets.newIdentityHashSet();
- for (DexEncodedMethod method : methods()) {
- boolean changed = unique.add(method.method);
- assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
- }
- return true;
- }
-
public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
- for (DexEncodedMethod method : directMethods()) {
- consumer.accept(method);
- }
- for (DexEncodedMethod method : virtualMethods()) {
- consumer.accept(method);
- }
+ methodCollection.forEachMethod(consumer);
}
- public DexEncodedMethod[] allMethodsSorted() {
- int vLen = virtualMethods.length;
- int dLen = directMethods.length;
- DexEncodedMethod[] result = new DexEncodedMethod[vLen + dLen];
- System.arraycopy(virtualMethods, 0, result, 0, vLen);
- System.arraycopy(directMethods, 0, result, vLen, dLen);
- Arrays.sort(result,
- (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
- }
-
- public DexEncodedMethod[] directMethodsSorted() {
- DexEncodedMethod[] result = new DexEncodedMethod[directMethods.length];
- System.arraycopy(directMethods, 0, result, 0, directMethods.length);
- Arrays.sort(
- result, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
- }
-
- public DexEncodedMethod[] virtualMethodsSorted() {
- DexEncodedMethod[] result = new DexEncodedMethod[virtualMethods.length];
- System.arraycopy(virtualMethods, 0, result, 0, virtualMethods.length);
- Arrays.sort(
- result, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.slowCompareTo(b.method));
- return result;
+ public List<DexEncodedMethod> allMethodsSorted() {
+ return methodCollection.allMethodsSorted();
}
public void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
- int vLen = virtualMethods.length;
- int dLen = directMethods.length;
- int mLen = privateInstanceMethods.size();
- assert mLen <= dLen;
-
- DexEncodedMethod[] newDirectMethods = new DexEncodedMethod[dLen - mLen];
- int index = 0;
- for (int i = 0; i < dLen; i++) {
- DexEncodedMethod encodedMethod = directMethods[i];
- if (!privateInstanceMethods.contains(encodedMethod)) {
- newDirectMethods[index++] = encodedMethod;
- }
- }
- assert index == dLen - mLen;
- setDirectMethods(newDirectMethods);
-
- DexEncodedMethod[] newVirtualMethods = new DexEncodedMethod[vLen + mLen];
- System.arraycopy(virtualMethods, 0, newVirtualMethods, 0, vLen);
- index = vLen;
- for (DexEncodedMethod encodedMethod : privateInstanceMethods) {
- newVirtualMethods[index++] = encodedMethod;
- }
- setVirtualMethods(newVirtualMethods);
+ methodCollection.virtualizeMethods(privateInstanceMethods);
}
/**
@@ -561,28 +398,27 @@
/** Find direct method in this class matching {@param method}. */
public DexEncodedMethod lookupDirectMethod(DexMethod method) {
- return lookupTarget(directMethods, method);
+ return methodCollection.getDirectMethod(method);
}
/** Find direct method in this class matching {@param predicate}. */
public DexEncodedMethod lookupDirectMethod(Predicate<DexEncodedMethod> predicate) {
- return PredicateUtils.findFirst(directMethods, predicate);
+ return methodCollection.getDirectMethod(predicate);
}
/** Find virtual method in this class matching {@param method}. */
public DexEncodedMethod lookupVirtualMethod(DexMethod method) {
- return lookupTarget(virtualMethods, method);
+ return methodCollection.getVirtualMethod(method);
}
/** Find virtual method in this class matching {@param predicate}. */
public DexEncodedMethod lookupVirtualMethod(Predicate<DexEncodedMethod> predicate) {
- return PredicateUtils.findFirst(virtualMethods, predicate);
+ return methodCollection.getVirtualMethod(predicate);
}
/** Find method in this class matching {@param method}. */
public DexEncodedMethod lookupMethod(DexMethod method) {
- DexEncodedMethod result = lookupDirectMethod(method);
- return result == null ? lookupVirtualMethod(method) : result;
+ return methodCollection.getMethod(method);
}
public DexEncodedMethod lookupSignaturePolymorphicMethod(
@@ -592,7 +428,7 @@
}
DexEncodedMethod matchingName = null;
DexEncodedMethod signaturePolymorphicMethod = null;
- for (DexEncodedMethod method : virtualMethods) {
+ for (DexEncodedMethod method : virtualMethods()) {
if (method.method.name == methodName) {
if (matchingName != null) {
// The jvm spec, section 5.4.3.3 details that there must be exactly one method with the
@@ -701,16 +537,7 @@
}
public synchronized DexEncodedMethod getClassInitializer() {
- if (cachedClassInitializer == null) {
- cachedClassInitializer = Optional.empty();
- for (DexEncodedMethod directMethod : directMethods) {
- if (directMethod.isClassInitializer()) {
- cachedClassInitializer = Optional.of(directMethod);
- break;
- }
- }
- }
- return cachedClassInitializer.orElse(null);
+ return methodCollection.getClassInitializer();
}
public Origin getOrigin() {
@@ -1004,8 +831,7 @@
assert !isInterface() || virtualMethods().stream().noneMatch(DexEncodedMethod::isFinal);
assert verifyCorrectnessOfFieldHolders(fields());
assert verifyNoDuplicateFields();
- assert verifyCorrectnessOfMethodHolders(methods());
- assert verifyNoDuplicateMethods();
+ assert methodCollection.verify();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index c0c2d6e..f5c5272 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -63,7 +63,7 @@
assert sorted == sortedHashCode();
return;
}
- Arrays.sort(elements, (a, b) -> a.name.compareTo(b.name));
+ Arrays.sort(elements, (a, b) -> a.name.slowCompareTo(b.name));
for (DexAnnotationElement element : elements) {
element.value.sort();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index e8bd9dc..8a21e7b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -80,6 +80,10 @@
return kotlinMemberInfo.memberKind.isBackingField();
}
+ public boolean isKotlinBackingFieldForCompanionObject() {
+ return kotlinMemberInfo.memberKind.isBackingFieldForCompanionObject();
+ }
+
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
@@ -134,10 +138,18 @@
return accessFlags.isStatic();
}
+ public boolean isPackagePrivate() {
+ return accessFlags.isPackagePrivate();
+ }
+
public boolean isPrivate() {
return accessFlags.isPrivate();
}
+ public boolean isProtected() {
+ return accessFlags.isProtected();
+ }
+
public boolean isPublic() {
return accessFlags.isPublic();
}
@@ -254,4 +266,19 @@
: DefaultFieldOptimizationInfo.getInstance();
return result;
}
+
+ public boolean validateDexValue(DexItemFactory factory) {
+ if (!accessFlags.isStatic() || staticValue == null) {
+ return true;
+ }
+ if (field.type.isPrimitiveType()) {
+ assert staticValue.getType(factory) == field.type
+ : "Static " + field + " has invalid static value " + staticValue + ".";
+ }
+ if (staticValue.isDexValueNull()) {
+ assert field.type.isReferenceType() : "Static " + field + " has invalid null static value.";
+ }
+ // TODO(b/150593449): Support non primitive DexValue (String, enum) and add assertions.
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 02cb985..d10a2c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -72,11 +72,6 @@
}
@Override
- public int compareTo(DexField other) {
- return sortedCompareTo(other.getSortedIndex());
- }
-
- @Override
public int slowCompareTo(DexField other) {
int result = holder.slowCompareTo(other.holder);
if (result != 0) {
@@ -103,19 +98,6 @@
}
@Override
- public int layeredCompareTo(DexField other, NamingLens namingLens) {
- int result = holder.compareTo(other.holder);
- if (result != 0) {
- return result;
- }
- result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
- if (result != 0) {
- return result;
- }
- return type.compareTo(other.type);
- }
-
- @Override
public boolean match(DexField field) {
return field.name == name && field.type == type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 58b768d..bfaf19c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -27,7 +27,6 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.LRUCacheTable;
import com.android.tools.r8.utils.Pair;
@@ -44,7 +43,6 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -572,7 +570,7 @@
createString("makeConcat")
);
- public final Set<DexMethod> libraryMethodsReturningReceiver =
+ public Set<DexMethod> libraryMethodsReturningReceiver =
ImmutableSet.<DexMethod>builder()
.addAll(stringBufferMethods.appendMethods)
.addAll(stringBuilderMethods.appendMethods)
@@ -1725,39 +1723,6 @@
);
}
- private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
- NamingLens namingLens) {
- List<S> sorted = new ArrayList<>(items);
- sorted.sort((a, b) -> a.layeredCompareTo(b, namingLens));
- int i = 0;
- for (S value : sorted) {
- value.setSortedIndex(i++);
- }
- }
-
- synchronized public void sort(NamingLens namingLens) {
- assert !sorted;
- assignSortedIndices(strings.values(), namingLens);
- assignSortedIndices(types.values(), namingLens);
- assignSortedIndices(fields.values(), namingLens);
- assignSortedIndices(protos.values(), namingLens);
- assignSortedIndices(methods.values(), namingLens);
- sorted = true;
- }
-
- synchronized public void resetSortedIndices() {
- if (!sorted) {
- return;
- }
- // Only used for asserting that we don't use the sorted index after we build the graph.
- strings.values().forEach(IndexedDexItem::resetSortedIndex);
- types.values().forEach(IndexedDexItem::resetSortedIndex);
- fields.values().forEach(IndexedDexItem::resetSortedIndex);
- protos.values().forEach(IndexedDexItem::resetSortedIndex);
- methods.values().forEach(IndexedDexItem::resetSortedIndex);
- sorted = false;
- }
-
synchronized public void forAllTypes(Consumer<DexType> f) {
new ArrayList<>(types.values()).forEach(f);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index a29eb31..80605e5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -6,17 +6,12 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.naming.NamingLens;
-import com.google.common.collect.Maps;
-import java.util.Map;
public class DexMethod extends DexMember<DexEncodedMethod, DexMethod> {
public final DexProto proto;
public final DexString name;
- // Caches used during processing.
- private Map<DexType, DexEncodedMethod> singleTargetCache;
-
DexMethod(DexType holder, DexProto proto, DexString name, boolean skipNameValidationForTesting) {
super(holder);
this.proto = proto;
@@ -102,11 +97,6 @@
}
@Override
- public int compareTo(DexMethod other) {
- return sortedCompareTo(other.getSortedIndex());
- }
-
- @Override
public int slowCompareTo(DexMethod other) {
int result = holder.slowCompareTo(other.holder);
if (result != 0) {
@@ -133,19 +123,6 @@
}
@Override
- public int layeredCompareTo(DexMethod other, NamingLens namingLens) {
- int result = holder.compareTo(other.holder);
- if (result != 0) {
- return result;
- }
- result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
- if (result != 0) {
- return result;
- }
- return proto.compareTo(other.proto);
- }
-
- @Override
public boolean match(DexMethod method) {
return method.name == name && method.proto == proto;
}
@@ -192,21 +169,4 @@
return name == dexItemFactory.deserializeLambdaMethodName
&& proto == dexItemFactory.deserializeLambdaMethodProto;
}
-
- synchronized public void setSingleVirtualMethodCache(
- DexType receiverType, DexEncodedMethod method) {
- if (singleTargetCache == null) {
- singleTargetCache = Maps.newIdentityHashMap();
- }
- singleTargetCache.put(receiverType, method);
- }
-
- synchronized public boolean isSingleVirtualMethodCached(DexType receiverType) {
- return singleTargetCache != null && singleTargetCache.containsKey(receiverType);
- }
-
- synchronized public DexEncodedMethod getSingleVirtualMethodCache(DexType receiverType) {
- assert isSingleVirtualMethodCached(receiverType);
- return singleTargetCache.get(receiverType);
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index fbe8a9f..a4be032 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -339,25 +339,6 @@
return result;
}
- @Override
- public int layeredCompareTo(DexMethodHandle other, NamingLens namingLens) {
- int result = type.getValue() - other.type.getValue();
- if (result == 0) {
- if (isFieldHandle()) {
- result = asField().layeredCompareTo(other.asField(), namingLens);
- } else {
- assert isMethodHandle();
- result = asMethod().layeredCompareTo(other.asMethod(), namingLens);
- }
- }
- return result;
- }
-
- @Override
- public int compareTo(DexMethodHandle other) {
- return slowCompareTo(other);
- }
-
public Handle toAsmHandle(NamingLens lens) {
String owner;
String name;
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 78a59c2..221a19e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -9,13 +9,13 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -34,7 +34,6 @@
new DexEncodedArray(DexValue.EMPTY_ARRAY);
private final ProgramResource.Kind originKind;
- private DexEncodedArray staticValues = SENTINEL_NOT_YET_COMPUTED;
private final Collection<DexProgramClass> synthesizedFrom;
private int initialClassFileVersion = -1;
private KotlinInfo kotlinInfo = null;
@@ -160,8 +159,9 @@
}
synchronizedCollectAll(indexedItems, staticFields);
synchronizedCollectAll(indexedItems, instanceFields);
- synchronizedCollectAll(indexedItems, directMethods);
- synchronizedCollectAll(indexedItems, virtualMethods);
+ synchronized (methodCollection) {
+ methodCollection.forEachMethod(m -> m.collectIndexedItems(indexedItems));
+ }
}
}
@@ -192,14 +192,9 @@
// We only have a class data item if there are methods or fields.
if (hasMethodsOrFields()) {
collector.add(this);
-
- // The ordering of methods and fields may not be deterministic due to concurrency
- // (see b/116027780).
- sortMembers();
- synchronizedCollectAll(collector, directMethods);
- synchronizedCollectAll(collector, virtualMethods);
- synchronizedCollectAll(collector, staticFields);
- synchronizedCollectAll(collector, instanceFields);
+ methodCollection.forEachMethod(m -> m.collectMixedSectionItems(collector));
+ collectAll(collector, staticFields);
+ collectAll(collector, instanceFields);
}
annotations().collectMixedSectionItems(collector);
if (interfaces != null) {
@@ -207,13 +202,6 @@
}
}
- private static <T extends DexItem> void synchronizedCollectAll(MixedSectionCollection collection,
- T[] items) {
- synchronized (items) {
- collectAll(collection, items);
- }
- }
-
@Override
public String toString() {
return type.toString();
@@ -278,7 +266,7 @@
}
public boolean hasMethods() {
- return directMethods.length + virtualMethods.length > 0;
+ return methodCollection.size() > 0;
}
public boolean hasMethodsOrFields() {
@@ -287,15 +275,13 @@
public boolean hasAnnotations() {
return !annotations().isEmpty()
- || hasAnnotations(virtualMethods)
- || hasAnnotations(directMethods)
+ || hasAnnotations(methodCollection)
|| hasAnnotations(staticFields)
|| hasAnnotations(instanceFields);
}
boolean hasOnlyInternalizableAnnotations() {
- return !hasAnnotations(virtualMethods)
- && !hasAnnotations(directMethods)
+ return !hasAnnotations(methodCollection)
&& !hasAnnotations(staticFields)
&& !hasAnnotations(instanceFields);
}
@@ -306,9 +292,9 @@
}
}
- private boolean hasAnnotations(DexEncodedMethod[] methods) {
+ private boolean hasAnnotations(MethodCollection methods) {
synchronized (methods) {
- return Arrays.stream(methods).anyMatch(DexEncodedMethod::hasAnnotation);
+ return methods.hasAnnotations();
}
}
@@ -320,97 +306,49 @@
}
}
- public void computeStaticValues() {
- // It does not actually hurt to compute this multiple times. So racing on staticValues is OK.
- if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
- synchronized (staticFields) {
- assert PresortedComparable.isSorted(Arrays.asList(staticFields));
- DexEncodedField[] fields = staticFields;
- int length = 0;
- List<DexValue> values = new ArrayList<>(fields.length);
- for (int i = 0; i < fields.length; i++) {
- DexEncodedField field = fields[i];
- DexValue staticValue = field.getStaticValue();
- assert staticValue != null;
- values.add(staticValue);
- if (!staticValue.isDefault(field.field.type)) {
- length = i + 1;
- }
- }
- staticValues =
- length > 0
- ? new DexEncodedArray(values.subList(0, length).toArray(DexValue.EMPTY_ARRAY))
- : null;
- }
- }
- }
-
- public boolean isSorted() {
- return isSorted(virtualMethods)
- && isSorted(directMethods)
- && isSorted(staticFields)
- && isSorted(instanceFields);
- }
-
- private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> boolean isSorted(
- D[] items) {
- synchronized (items) {
- D[] sorted = items.clone();
- Arrays.sort(sorted, Comparator.comparing(DexEncodedMember::toReference));
- return Arrays.equals(items, sorted);
- }
- }
- public DexEncodedArray getStaticValues() {
- // The sentinel value is left over for classes that actually have no fields.
- if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
- assert !hasMethodsOrFields();
+ public DexEncodedArray computeStaticValuesArray(NamingLens namingLens) {
+ // Fast path to avoid sorting and collection allocation when no non-default values exist.
+ if (!hasNonDefaultStaticFieldValues()) {
return null;
}
- return staticValues;
+ DexEncodedField[] fields = staticFields;
+ Arrays.sort(fields, (a, b) -> a.field.slowCompareTo(b.field, namingLens));
+ int length = 0;
+ List<DexValue> values = new ArrayList<>(fields.length);
+ for (int i = 0; i < fields.length; i++) {
+ DexEncodedField field = fields[i];
+ DexValue staticValue = field.getStaticValue();
+ assert staticValue != null;
+ values.add(staticValue);
+ if (!staticValue.isDefault(field.field.type)) {
+ length = i + 1;
+ }
+ }
+ return length > 0
+ ? new DexEncodedArray(values.subList(0, length).toArray(DexValue.EMPTY_ARRAY))
+ : null;
+ }
+
+ private boolean hasNonDefaultStaticFieldValues() {
+ for (DexEncodedField field : staticFields) {
+ DexValue value = field.getStaticValue();
+ if (value != null && !value.isDefault(field.field.type)) {
+ return true;
+ }
+ }
+ return false;
}
public void addMethod(DexEncodedMethod method) {
- if (method.accessFlags.isStatic()
- || method.accessFlags.isPrivate()
- || method.accessFlags.isConstructor()) {
- addDirectMethod(method);
- } else {
- addVirtualMethod(method);
- }
+ methodCollection.addMethod(method);
}
public void addVirtualMethod(DexEncodedMethod virtualMethod) {
- assert !virtualMethod.accessFlags.isStatic();
- assert !virtualMethod.accessFlags.isPrivate();
- assert !virtualMethod.accessFlags.isConstructor();
- virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
- virtualMethods[virtualMethods.length - 1] = virtualMethod;
+ methodCollection.addVirtualMethod(virtualMethod);
}
- public void addDirectMethod(DexEncodedMethod staticMethod) {
- assert staticMethod.accessFlags.isStatic() || staticMethod.accessFlags.isPrivate()
- || staticMethod.accessFlags.isConstructor();
- directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
- directMethods[directMethods.length - 1] = staticMethod;
- }
-
- public void sortMembers() {
- sortEncodedFields(staticFields);
- sortEncodedFields(instanceFields);
- sortEncodedMethods(directMethods);
- sortEncodedMethods(virtualMethods);
- }
-
- private void sortEncodedFields(DexEncodedField[] fields) {
- synchronized (fields) {
- Arrays.sort(fields, Comparator.comparing(a -> a.field));
- }
- }
-
- private void sortEncodedMethods(DexEncodedMethod[] methods) {
- synchronized (methods) {
- Arrays.sort(methods, Comparator.comparing(a -> a.method));
- }
+ public void addDirectMethod(DexEncodedMethod directMethod) {
+ methodCollection.addDirectMethod(directMethod);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 43d6a29..a9a15f3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -63,11 +63,6 @@
}
@Override
- public int compareTo(DexProto other) {
- return sortedCompareTo(other.getSortedIndex());
- }
-
- @Override
public int slowCompareTo(DexProto other) {
int result = returnType.slowCompareTo(other.returnType);
if (result == 0) {
@@ -86,15 +81,6 @@
}
@Override
- public int layeredCompareTo(DexProto other, NamingLens namingLens) {
- int result = returnType.compareTo(other.returnType);
- if (result == 0) {
- result = parameters.compareTo(other.parameters);
- }
- return result;
- }
-
- @Override
public String toSmaliString() {
return toDescriptorString();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index f3a829c..03303f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -251,11 +251,6 @@
}
@Override
- public int compareTo(DexString other) {
- return sortedCompareTo(other.getSortedIndex());
- }
-
- @Override
public int slowCompareTo(DexString other) {
// Compare the bytes, as comparing UTF-8 encoded strings as strings of unsigned bytes gives
// the same result as comparing the corresponding Unicode strings lexicographically by
@@ -294,12 +289,6 @@
return slowCompareTo(other);
}
- @Override
- public int layeredCompareTo(DexString other, NamingLens lens) {
- // Strings have no subparts that are already sorted.
- return slowCompareTo(other);
- }
-
private static boolean isValidClassDescriptor(String string) {
if (string.length() < 3
|| string.charAt(0) != 'L'
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index c046e6a..4b8c6d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -175,11 +175,6 @@
}
@Override
- public int compareTo(DexType other) {
- return sortedCompareTo(other.getSortedIndex());
- }
-
- @Override
public int slowCompareTo(DexType other) {
return descriptor.slowCompareTo(other.descriptor);
}
@@ -188,14 +183,7 @@
public int slowCompareTo(DexType other, NamingLens namingLens) {
DexString thisDescriptor = namingLens.lookupDescriptor(this);
DexString otherDescriptor = namingLens.lookupDescriptor(other);
- return thisDescriptor.slowCompareTo(otherDescriptor);
- }
-
- @Override
- public int layeredCompareTo(DexType other, NamingLens namingLens) {
- DexString thisDescriptor = namingLens.lookupDescriptor(this);
- DexString otherDescriptor = namingLens.lookupDescriptor(other);
- return thisDescriptor.compareTo(otherDescriptor);
+ return thisDescriptor.slowCompareTo(otherDescriptor, namingLens);
}
public boolean isPrimitiveType() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index a124bc4..58b6afd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.naming.NamingLens;
import java.util.Arrays;
-public class DexTypeList extends DexItem implements Comparable<DexTypeList> {
+public class DexTypeList extends DexItem {
private static final DexTypeList theEmptyTypeList = new DexTypeList();
@@ -75,23 +75,6 @@
return builder.toString();
}
- @Override
- public int compareTo(DexTypeList other) {
- for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
- if (i == values.length) {
- return i == other.values.length ? 0 : -1;
- } else if (i == other.values.length) {
- return 1;
- } else {
- int result = values[i].compareTo(other.values[i]);
- if (result != 0) {
- return result;
- }
- }
- }
- throw new Unreachable();
- }
-
public int slowCompareTo(DexTypeList other) {
for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
if (i == values.length) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 794f7c8..be0cf37 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -304,6 +304,8 @@
}
}
+ public abstract DexType getType(DexItemFactory factory);
+
public abstract Object getBoxedValue();
/** Returns an instruction that can be used to materialize this {@link DexValue} (or null). */
@@ -390,6 +392,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.byteType;
+ }
+
+ @Override
public long getRawValue() {
return value;
}
@@ -463,6 +470,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.shortType;
+ }
+
+ @Override
public long getRawValue() {
return value;
}
@@ -535,6 +547,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.charType;
+ }
+
+ @Override
public long getRawValue() {
return value;
}
@@ -611,6 +628,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.intType;
+ }
+
+ @Override
public long getRawValue() {
return value;
}
@@ -683,6 +705,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.longType;
+ }
+
+ @Override
public long getRawValue() {
return value;
}
@@ -755,6 +782,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.floatType;
+ }
+
+ @Override
public long getRawValue() {
return Float.floatToIntBits(value);
}
@@ -833,6 +865,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.doubleType;
+ }
+
+ @Override
public long getRawValue() {
return Double.doubleToRawLongBits(value);
}
@@ -902,6 +939,11 @@
protected abstract DexValueKind getValueKind();
+ @Override
+ public DexType getType(DexItemFactory factory) {
+ throw new Unreachable();
+ }
+
public T getValue() {
return value;
}
@@ -987,6 +1029,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.stringType;
+ }
+
+ @Override
public ConstInstruction asConstInstruction(
AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
TypeLatticeElement type = TypeLatticeElement.stringClassType(appView, definitelyNotNull());
@@ -1040,6 +1087,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.stringType;
+ }
+
+ @Override
public ConstInstruction asConstInstruction(
AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
TypeLatticeElement type = TypeLatticeElement.stringClassType(appView, definitelyNotNull());
@@ -1195,6 +1247,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ throw new Unreachable();
+ }
+
+ @Override
public Object getBoxedValue() {
throw new Unreachable("No boxed value for DexValueArray");
}
@@ -1265,6 +1322,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ throw new Unreachable();
+ }
+
+ @Override
public Object getBoxedValue() {
throw new Unreachable("No boxed value for DexValueAnnotation");
}
@@ -1315,6 +1377,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ throw new Unreachable();
+ }
+
+ @Override
public long getRawValue() {
return 0;
}
@@ -1391,6 +1458,11 @@
}
@Override
+ public DexType getType(DexItemFactory factory) {
+ return factory.booleanType;
+ }
+
+ @Override
public long getRawValue() {
return BooleanUtils.longValue(value);
}
diff --git a/src/main/java/com/android/tools/r8/graph/FinalInitClassLens.java b/src/main/java/com/android/tools/r8/graph/FinalInitClassLens.java
new file mode 100644
index 0000000..4f0a854
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FinalInitClassLens.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import java.util.Map;
+
+public class FinalInitClassLens extends InitClassLens {
+
+ private final Map<DexType, DexField> mapping;
+
+ FinalInitClassLens(Map<DexType, DexField> mapping) {
+ this.mapping = mapping;
+ }
+
+ @Override
+ public DexField getInitClassField(DexType type) {
+ DexField field = mapping.get(type);
+ if (field != null) {
+ return field;
+ }
+ throw new Unreachable("Unexpected InitClass instruction for `" + type.toSourceString() + "`");
+ }
+
+ @Override
+ public boolean isFinal() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
index 68a0e5d..0c115e1 100644
--- a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -6,13 +6,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
-/**
- * Subset of dex items that are referenced by some table index.
- */
-public abstract class IndexedDexItem extends CachedHashValueDexItem implements Presorted {
-
- private static final int SORTED_INDEX_UNKNOWN = -1;
- private int sortedIndex = SORTED_INDEX_UNKNOWN; // assigned globally after reading.
+/** Subset of dex items that are referenced by some table index. */
+public abstract class IndexedDexItem extends CachedHashValueDexItem {
@Override
public abstract void collectIndexedItems(IndexedItemCollection indexedItems,
@@ -29,32 +24,7 @@
// Partial implementation of PresortedComparable.
@Override
- final public void setSortedIndex(int sortedIndex) {
- assert sortedIndex > SORTED_INDEX_UNKNOWN;
- assert this.sortedIndex == SORTED_INDEX_UNKNOWN;
- this.sortedIndex = sortedIndex;
- }
-
- @Override
- final public int getSortedIndex() {
- return sortedIndex;
- }
-
- @Override
- final public int sortedCompareTo(int other) {
- assert sortedIndex > SORTED_INDEX_UNKNOWN
- : "sortedIndex <= SORTED_INDEX_UKNOWN for: " + this.toString();
- assert other > SORTED_INDEX_UNKNOWN : "other < SORTED_INDEX_UKNOWN for: " + this.toString();
- return Integer.compare(sortedIndex, other);
- }
-
- @Override
public void flushCachedValues() {
super.flushCachedValues();
- resetSortedIndex();
- }
-
- public void resetSortedIndex() {
- sortedIndex = SORTED_INDEX_UNKNOWN;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/InitClassLens.java b/src/main/java/com/android/tools/r8/graph/InitClassLens.java
new file mode 100644
index 0000000..cac3870
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/InitClassLens.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class InitClassLens {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static DefaultInitClassLens getDefault() {
+ return DefaultInitClassLens.getInstance();
+ }
+
+ public abstract DexField getInitClassField(DexType clazz);
+
+ public boolean isFinal() {
+ return false;
+ }
+
+ public static class Builder {
+
+ private final Map<DexType, DexField> mapping = new ConcurrentHashMap<>();
+
+ public void map(DexType type, DexField field) {
+ assert field.holder == type;
+ mapping.put(type, field);
+ }
+
+ public FinalInitClassLens build() {
+ return new FinalInitClassLens(mapping);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
new file mode 100644
index 0000000..7800d81a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -0,0 +1,300 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class MethodArrayBacking extends MethodCollectionBacking {
+
+ private DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ private DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+
+ private boolean belongsToDirectPool(DexEncodedMethod method) {
+ return method.accessFlags.isStatic()
+ || method.accessFlags.isPrivate()
+ || method.accessFlags.isConstructor();
+ }
+
+ private boolean belongsToVirtualPool(DexEncodedMethod method) {
+ return !belongsToDirectPool(method);
+ }
+
+ int size() {
+ return directMethods.length + virtualMethods.length;
+ }
+
+ @Override
+ TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+ for (DexEncodedMethod method : directMethods) {
+ TraversalContinuation stepResult = fn.apply(method);
+ if (stepResult.shouldBreak()) {
+ return stepResult;
+ }
+ }
+ for (DexEncodedMethod method : virtualMethods) {
+ TraversalContinuation stepResult = fn.apply(method);
+ if (stepResult.shouldBreak()) {
+ return stepResult;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
+
+ public Iterable<DexEncodedMethod> methods() {
+ return Iterables.concat(Arrays.asList(directMethods), Arrays.asList(virtualMethods));
+ }
+
+ List<DexEncodedMethod> directMethods() {
+ assert directMethods != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(directMethods));
+ }
+ return Arrays.asList(directMethods);
+ }
+
+ void appendDirectMethod(DexEncodedMethod method) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + 1];
+ System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
+ newMethods[directMethods.length] = method;
+ directMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void appendDirectMethods(Collection<DexEncodedMethod> methods) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length + methods.size()];
+ System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
+ int i = directMethods.length;
+ for (DexEncodedMethod method : methods) {
+ newMethods[i] = method;
+ i++;
+ }
+ directMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ private void removeDirectMethod(int index) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[directMethods.length - 1];
+ System.arraycopy(directMethods, 0, newMethods, 0, index);
+ System.arraycopy(directMethods, index + 1, newMethods, index, directMethods.length - index - 1);
+ directMethods = newMethods;
+ }
+
+ void removeDirectMethod(DexMethod method) {
+ int index = -1;
+ for (int i = 0; i < directMethods.length; i++) {
+ if (method.match(directMethods[i])) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ removeDirectMethod(index);
+ }
+ }
+
+ void setDirectMethod(int index, DexEncodedMethod method) {
+ directMethods[index] = method;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void setDirectMethods(DexEncodedMethod[] methods) {
+ directMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
+ assert verifyNoDuplicateMethods();
+ }
+
+ List<DexEncodedMethod> virtualMethods() {
+ assert virtualMethods != null;
+ if (InternalOptions.assertionsEnabled()) {
+ return Collections.unmodifiableList(Arrays.asList(virtualMethods));
+ }
+ return Arrays.asList(virtualMethods);
+ }
+
+ void appendVirtualMethod(DexEncodedMethod method) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + 1];
+ System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
+ newMethods[virtualMethods.length] = method;
+ virtualMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[virtualMethods.length + methods.size()];
+ System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
+ int i = virtualMethods.length;
+ for (DexEncodedMethod method : methods) {
+ newMethods[i] = method;
+ i++;
+ }
+ virtualMethods = newMethods;
+ assert verifyNoDuplicateMethods();
+ }
+
+ void setVirtualMethods(DexEncodedMethod[] methods) {
+ virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
+ assert verifyNoDuplicateMethods();
+ }
+
+ void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
+ int vLen = virtualMethods.length;
+ int dLen = directMethods.length;
+ int mLen = privateInstanceMethods.size();
+ assert mLen <= dLen;
+
+ DexEncodedMethod[] newDirectMethods = new DexEncodedMethod[dLen - mLen];
+ int index = 0;
+ for (int i = 0; i < dLen; i++) {
+ DexEncodedMethod encodedMethod = directMethods[i];
+ if (!privateInstanceMethods.contains(encodedMethod)) {
+ newDirectMethods[index++] = encodedMethod;
+ }
+ }
+ assert index == dLen - mLen;
+ setDirectMethods(newDirectMethods);
+
+ DexEncodedMethod[] newVirtualMethods = new DexEncodedMethod[vLen + mLen];
+ System.arraycopy(virtualMethods, 0, newVirtualMethods, 0, vLen);
+ index = vLen;
+ for (DexEncodedMethod encodedMethod : privateInstanceMethods) {
+ newVirtualMethods[index++] = encodedMethod;
+ }
+ setVirtualMethods(newVirtualMethods);
+ }
+
+ DexEncodedMethod getDirectMethod(DexMethod method) {
+ for (DexEncodedMethod directMethod : directMethods) {
+ if (method.match(directMethod)) {
+ return directMethod;
+ }
+ }
+ return null;
+ }
+
+ DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
+ return PredicateUtils.findFirst(directMethods, predicate);
+ }
+
+ DexEncodedMethod getVirtualMethod(DexMethod method) {
+ for (DexEncodedMethod virtualMethod : virtualMethods) {
+ if (method.match(virtualMethod)) {
+ return virtualMethod;
+ }
+ }
+ return null;
+ }
+
+ DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
+ return PredicateUtils.findFirst(virtualMethods, predicate);
+ }
+
+ DexEncodedMethod getMethod(DexMethod method) {
+ DexEncodedMethod result = getDirectMethod(method);
+ return result == null ? getVirtualMethod(method) : result;
+ }
+
+ void addMethod(DexEncodedMethod method) {
+ if (method.accessFlags.isStatic()
+ || method.accessFlags.isPrivate()
+ || method.accessFlags.isConstructor()) {
+ addDirectMethod(method);
+ } else {
+ addVirtualMethod(method);
+ }
+ }
+
+ void addVirtualMethod(DexEncodedMethod virtualMethod) {
+ assert !virtualMethod.accessFlags.isStatic();
+ assert !virtualMethod.accessFlags.isPrivate();
+ assert !virtualMethod.accessFlags.isConstructor();
+ virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
+ virtualMethods[virtualMethods.length - 1] = virtualMethod;
+ }
+
+ void addDirectMethod(DexEncodedMethod staticMethod) {
+ assert staticMethod.accessFlags.isStatic()
+ || staticMethod.accessFlags.isPrivate()
+ || staticMethod.accessFlags.isConstructor();
+ directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
+ directMethods[directMethods.length - 1] = staticMethod;
+ }
+
+ boolean verifyNoDuplicateMethods() {
+ Set<DexMethod> unique = Sets.newIdentityHashSet();
+ forEachMethod(
+ method -> {
+ boolean changed = unique.add(method.method);
+ assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
+ });
+ return true;
+ }
+
+ public DexEncodedMethod replaceDirectMethod(
+ DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ for (int i = 0; i < directMethods.length; i++) {
+ DexEncodedMethod directMethod = directMethods[i];
+ if (method.match(directMethod)) {
+ DexEncodedMethod newMethod = replacement.apply(directMethod);
+ assert belongsToDirectPool(newMethod);
+ directMethods[i] = newMethod;
+ return newMethod;
+ }
+ }
+ return null;
+ }
+
+ public DexEncodedMethod replaceDirectMethodWithVirtualMethod(
+ DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ for (int i = 0; i < directMethods.length; i++) {
+ DexEncodedMethod directMethod = directMethods[i];
+ if (method.match(directMethod)) {
+ DexEncodedMethod newMethod = replacement.apply(directMethod);
+ assert belongsToVirtualPool(newMethod);
+ removeDirectMethod(i);
+ appendVirtualMethod(newMethod);
+ return newMethod;
+ }
+ }
+ return null;
+ }
+
+ public void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ replaceDirectMethods(replacement);
+ replaceVirtualMethods(replacement);
+ }
+
+ public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ for (int i = 0; i < directMethods.length; i++) {
+ DexEncodedMethod method = directMethods[i];
+ DexEncodedMethod newMethod = replacement.apply(method);
+ assert newMethod != null;
+ if (method != newMethod) {
+ assert belongsToDirectPool(newMethod);
+ directMethods[i] = newMethod;
+ }
+ }
+ }
+
+ public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ for (int i = 0; i < virtualMethods.length; i++) {
+ DexEncodedMethod method = virtualMethods[i];
+ DexEncodedMethod newMethod = replacement.apply(method);
+ if (method != newMethod) {
+ assert belongsToVirtualPool(newMethod);
+ virtualMethods[i] = newMethod;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
new file mode 100644
index 0000000..a4f3d5a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -0,0 +1,221 @@
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class MethodCollection {
+
+ private final DexClass holder;
+ private final MethodArrayBacking backing = new MethodArrayBacking();
+ private Optional<DexEncodedMethod> cachedClassInitializer = null;
+
+ public MethodCollection(DexClass holder) {
+ this.holder = holder;
+ }
+
+ public int size() {
+ return backing.size();
+ }
+
+ public TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn) {
+ return backing.traverse(fn);
+ }
+
+ public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
+ backing.forEachMethod(consumer);
+ }
+
+ public Iterable<DexEncodedMethod> methods() {
+ return backing.methods();
+ }
+
+ public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+ return IterableUtils.filter(methods(), predicate);
+ }
+
+ public List<DexEncodedMethod> allMethodsSorted() {
+ List<DexEncodedMethod> sorted = new ArrayList<>(size());
+ forEachMethod(sorted::add);
+ sorted.sort((a, b) -> a.method.slowCompareTo(b.method));
+ return sorted;
+ }
+
+ public List<DexEncodedMethod> directMethods() {
+ return backing.directMethods();
+ }
+
+ public List<DexEncodedMethod> virtualMethods() {
+ return backing.virtualMethods();
+ }
+
+ public DexEncodedMethod getMethod(DexMethod method) {
+ return backing.getMethod(method);
+ }
+
+ public DexEncodedMethod getDirectMethod(DexMethod method) {
+ return backing.getDirectMethod(method);
+ }
+
+ public DexEncodedMethod getDirectMethod(Predicate<DexEncodedMethod> predicate) {
+ return backing.getDirectMethod(predicate);
+ }
+
+ public DexEncodedMethod getVirtualMethod(DexMethod method) {
+ return backing.getVirtualMethod(method);
+ }
+
+ public DexEncodedMethod getVirtualMethod(Predicate<DexEncodedMethod> predicate) {
+ return backing.getVirtualMethod(predicate);
+ }
+
+ public DexEncodedMethod getClassInitializer() {
+ if (cachedClassInitializer == null) {
+ cachedClassInitializer = Optional.empty();
+ for (DexEncodedMethod directMethod : directMethods()) {
+ if (directMethod.isClassInitializer()) {
+ cachedClassInitializer = Optional.of(directMethod);
+ break;
+ }
+ }
+ }
+ return cachedClassInitializer.orElse(null);
+ }
+
+ public void addMethod(DexEncodedMethod method) {
+ backing.addMethod(method);
+ }
+
+ public void addVirtualMethod(DexEncodedMethod virtualMethod) {
+ backing.addVirtualMethod(virtualMethod);
+ }
+
+ public void addDirectMethod(DexEncodedMethod directMethod) {
+ cachedClassInitializer = null;
+ backing.addDirectMethod(directMethod);
+ }
+
+ public DexEncodedMethod replaceDirectMethod(
+ DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ cachedClassInitializer = null;
+ return backing.replaceDirectMethod(method, replacement);
+ }
+
+ public void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ backing.replaceMethods(replacement);
+ }
+
+ public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ backing.replaceVirtualMethods(replacement);
+ }
+
+ public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ cachedClassInitializer = null;
+ backing.replaceDirectMethods(replacement);
+ }
+
+ /**
+ * Replace a direct method, if found, by a computed virtual method using the replacement function.
+ *
+ * @param method Direct method to replace if present.
+ * @param replacement Replacement function computing the virtual replacement.
+ * @return Returns the replacement if found, null otherwise.
+ */
+ public DexEncodedMethod replaceDirectMethodWithVirtualMethod(
+ DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ // The class initializer can never by converted to a virtual.
+ return backing.replaceDirectMethodWithVirtualMethod(method, replacement);
+ }
+
+ public void appendDirectMethod(DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ cachedClassInitializer = null;
+ backing.appendDirectMethod(method);
+ }
+
+ public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ cachedClassInitializer = null;
+ backing.appendDirectMethods(methods);
+ }
+
+ public void removeDirectMethod(DexMethod method) {
+ cachedClassInitializer = null;
+ backing.removeDirectMethod(method);
+ }
+
+ public void setDirectMethods(DexEncodedMethod[] methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ cachedClassInitializer = null;
+ backing.setDirectMethods(methods);
+ }
+
+ public void appendVirtualMethod(DexEncodedMethod method) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ backing.appendVirtualMethod(method);
+ }
+
+ public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ backing.appendVirtualMethods(methods);
+ }
+
+ public void setVirtualMethods(DexEncodedMethod[] methods) {
+ assert verifyCorrectnessOfMethodHolders(methods);
+ backing.setVirtualMethods(methods);
+ }
+
+ public void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) {
+ backing.virtualizeMethods(privateInstanceMethods);
+ }
+
+ public boolean hasAnnotations() {
+ return traverse(
+ method ->
+ method.hasAnnotation()
+ ? TraversalContinuation.BREAK
+ : TraversalContinuation.CONTINUE)
+ .shouldBreak();
+ }
+
+ public boolean verify() {
+ forEachMethod(
+ method -> {
+ assert verifyCorrectnessOfMethodHolder(method);
+ });
+ assert backing.verifyNoDuplicateMethods();
+ return true;
+ }
+
+ private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
+ assert method.method.holder == holder.type
+ : "Expected method `"
+ + method.method.toSourceString()
+ + "` to have holder `"
+ + holder.type.toSourceString()
+ + "`";
+ return true;
+ }
+
+ private boolean verifyCorrectnessOfMethodHolders(DexEncodedMethod[] methods) {
+ if (methods == null) {
+ return true;
+ }
+ return verifyCorrectnessOfMethodHolders(Arrays.asList(methods));
+ }
+
+ private boolean verifyCorrectnessOfMethodHolders(Iterable<DexEncodedMethod> methods) {
+ for (DexEncodedMethod method : methods) {
+ assert verifyCorrectnessOfMethodHolder(method);
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
new file mode 100644
index 0000000..b50adf6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public abstract class MethodCollectionBacking {
+
+ abstract TraversalContinuation traverse(Function<DexEncodedMethod, TraversalContinuation> fn);
+
+ void forEachMethod(Consumer<DexEncodedMethod> fn) {
+ traverse(
+ method -> {
+ fn.accept(method);
+ return TraversalContinuation.CONTINUE;
+ });
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index a29ae60..33f3b90 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -5,14 +5,16 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.naming.NamingLens;
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap.Entry;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
-import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -21,18 +23,25 @@
private final static int NOT_FOUND = -1;
private final static int NOT_SET = -2;
+ private final NamingLens namingLens;
+ private final InitClassLens initClassLens;
+
+ // Sorted collection of objects mapped to their offsets.
private final DexProgramClass[] classes;
- private final Reference2IntMap<DexProto> protos;
- private final Reference2IntMap<DexType> types;
- private final Reference2IntMap<DexMethod> methods;
- private final Reference2IntMap<DexField> fields;
- private final Reference2IntMap<DexString> strings;
- private final Reference2IntMap<DexCallSite> callSites;
- private final Reference2IntMap<DexMethodHandle> methodHandles;
+ private final Reference2IntLinkedOpenHashMap<DexProto> protos;
+ private final Reference2IntLinkedOpenHashMap<DexType> types;
+ private final Reference2IntLinkedOpenHashMap<DexMethod> methods;
+ private final Reference2IntLinkedOpenHashMap<DexField> fields;
+ private final Reference2IntLinkedOpenHashMap<DexString> strings;
+ private final Reference2IntLinkedOpenHashMap<DexCallSite> callSites;
+ private final Reference2IntLinkedOpenHashMap<DexMethodHandle> methodHandles;
+
private DexString firstJumboString;
public ObjectToOffsetMapping(
DexApplication application,
+ NamingLens namingLens,
+ InitClassLens initClassLens,
Collection<DexProgramClass> classes,
Collection<DexProto> protos,
Collection<DexType> types,
@@ -50,15 +59,21 @@
assert strings != null;
assert callSites != null;
assert methodHandles != null;
+ assert initClassLens != null;
+ this.namingLens = namingLens;
+ this.initClassLens = initClassLens;
+ this.classes = sortClasses(application, classes, namingLens);
+ this.protos = createSortedMap(protos, compare(namingLens), this::failOnOverflow);
+ this.types = createSortedMap(types, compare(namingLens), this::failOnOverflow);
+ this.methods = createSortedMap(methods, compare(namingLens), this::failOnOverflow);
+ this.fields = createSortedMap(fields, compare(namingLens), this::failOnOverflow);
+ this.strings = createSortedMap(strings, compare(namingLens), this::setFirstJumboString);
+ this.callSites = createSortedMap(callSites, DexCallSite::compareTo, this::failOnOverflow);
+ this.methodHandles = createSortedMap(methodHandles, compare(namingLens), this::failOnOverflow);
+ }
- this.classes = sortClasses(application, classes);
- this.protos = createMap(protos, this::failOnOverflow);
- this.types = createMap(types, this::failOnOverflow);
- this.methods = createMap(methods, this::failOnOverflow);
- this.fields = createMap(fields, this::failOnOverflow);
- this.strings = createMap(strings, this::setFirstJumboString);
- this.callSites = createMap(callSites, this::failOnOverflow);
- this.methodHandles = createMap(methodHandles, this::failOnOverflow);
+ private static <T extends PresortedComparable<T>> Comparator<T> compare(NamingLens namingLens) {
+ return (a, b) -> a.slowCompareTo(b, namingLens);
}
private void setFirstJumboString(DexString string) {
@@ -70,14 +85,16 @@
throw new CompilationError("Index overflow for " + item.getClass());
}
- private <T extends IndexedDexItem> Reference2IntMap<T> createMap(Collection<T> items,
- Consumer<T> onUInt16Overflow) {
+ private <T> Reference2IntLinkedOpenHashMap<T> createSortedMap(
+ Collection<T> items, Comparator<T> comparator, Consumer<T> onUInt16Overflow) {
if (items.isEmpty()) {
return null;
}
- Reference2IntMap<T> map = new Reference2IntLinkedOpenHashMap<>(items.size());
+ // Sort items and compute the offset mapping for each in sorted order.
+ ArrayList<T> sorted = new ArrayList<>(items);
+ sorted.sort(comparator);
+ Reference2IntLinkedOpenHashMap<T> map = new Reference2IntLinkedOpenHashMap<>(items.size());
map.defaultReturnValue(NOT_FOUND);
- Collection<T> sorted = items.stream().sorted().collect(Collectors.toList());
int index = 0;
for (T item : sorted) {
if (index == Constants.U16BIT_MAX + 1) {
@@ -135,26 +152,30 @@
}
private static DexProgramClass[] sortClasses(
- DexApplication application, Collection<DexProgramClass> classes) {
+ DexApplication application, Collection<DexProgramClass> classes, NamingLens namingLens) {
// Collect classes in subtyping order, based on a sorted list of classes to start with.
ProgramClassDepthsMemoized classDepths = new ProgramClassDepthsMemoized(application);
List<DexProgramClass> sortedClasses =
- classes
- .stream()
+ classes.stream()
.sorted(
(x, y) -> {
int dx = classDepths.getDepth(x);
int dy = classDepths.getDepth(y);
- return dx != dy ? dx - dy : x.type.compareTo(y.type);
+ return dx != dy ? dx - dy : x.type.slowCompareTo(y.type, namingLens);
})
.collect(Collectors.toList());
return sortedClasses.toArray(DexProgramClass.EMPTY_ARRAY);
}
- private static <T> Collection<T> keysOrEmpty(Map<T, ?> map) {
+ private static <T> Collection<T> keysOrEmpty(Reference2IntLinkedOpenHashMap<T> map) {
+ // The key-set is deterministic (linked) and inserted in sorted order.
return map == null ? Collections.emptyList() : map.keySet();
}
+ public NamingLens getNamingLens() {
+ return namingLens;
+ }
+
public Collection<DexMethod> getMethods() {
return keysOrEmpty(methods);
}
@@ -238,4 +259,8 @@
public int getOffsetFor(DexMethodHandle methodHandle) {
return getOffsetFor(methodHandle, methodHandles);
}
+
+ public DexField getClinitField(DexType type) {
+ return initClassLens.getInitClassField(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/Presorted.java b/src/main/java/com/android/tools/r8/graph/Presorted.java
deleted file mode 100644
index 475da13..0000000
--- a/src/main/java/com/android/tools/r8/graph/Presorted.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
-
-/**
- * Interface for capturing presorted behavior for Dex items.
- */
-public interface Presorted {
-
- void setSortedIndex(int sortedIndex);
-
- int getSortedIndex();
-
- int sortedCompareTo(int other);
-}
diff --git a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
index 075c187..e4eb4b7 100644
--- a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
+++ b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
@@ -4,41 +4,11 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.NamingLens;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Function;
-public interface PresortedComparable<T> extends Presorted, Comparable<T> {
+public interface PresortedComparable<T> {
- static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> boolean isSorted(
- List<? extends DexEncodedMember<D, R>> items) {
- return isSorted(items, DexEncodedMember::toReference);
- }
-
- static <S, T extends Comparable<T>> boolean isSorted(S[] items, Function<S, T> getter) {
- return isSorted(Arrays.asList(items), getter);
- }
-
- static <S, T extends Comparable<T>> boolean isSorted(
- List<? extends S> items, Function<S, T> getter) {
- T current = null;
- for (S item : items) {
- T next = getter.apply(item);
- if (current != null && current.compareTo(next) >= 0) {
- return false;
- }
- current = next;
- }
- return true;
- }
-
- // Slow comparison methods that make no use of indices for comparisons. These are used
- // for sorting operations when reading dex files.
int slowCompareTo(T other);
int slowCompareTo(T other, NamingLens namingLens);
- // Layered comparison methods that make use of indices for subpart comparisons. These rely
- // on subparts already being sorted and having indices assigned.
- int layeredCompareTo(T other, NamingLens namingLens);
static <T extends PresortedComparable<T>> int slowCompare(T a, T b) {
return a.slowCompareTo(b);
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index f9cea40..5c1611d 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -410,14 +410,11 @@
// TODO(b/148769279): Remove the check for hasInstantiatedLambdas.
Box<Boolean> hasInstantiatedLambdas = new Box<>(false);
InstantiatedSubTypeInfo instantiatedSubTypeInfo =
- refinedReceiverLowerBound == null
- ? instantiatedSubTypeInfoWithoutLowerBound(
- appInfo, refinedReceiverUpperBound, hasInstantiatedLambdas)
- : instantiatedSubTypeInfoWithLowerBound(
- appInfo,
- refinedReceiverUpperBound,
- refinedReceiverLowerBound,
- hasInstantiatedLambdas);
+ instantiatedSubTypeInfoForInstantiatedType(
+ appInfo,
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound,
+ hasInstantiatedLambdas);
LookupResult lookupResult =
lookupVirtualDispatchTargets(
context,
@@ -430,37 +427,28 @@
return lookupResult;
}
- private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithoutLowerBound(
- AppInfoWithLiveness appInfo,
- DexProgramClass refinedReceiverUpperBound,
- Box<Boolean> hasInstantiatedLambdas) {
- return (type, subTypeConsumer, callSiteConsumer) -> {
- appInfo.forEachInstantiatedSubType(
- refinedReceiverUpperBound.type,
- subType -> {
- if (appInfo.hasAnyInstantiatedLambdas(subType)) {
- hasInstantiatedLambdas.set(true);
- }
- subTypeConsumer.accept(subType);
- },
- callSiteConsumer);
- };
- }
-
- private InstantiatedSubTypeInfo instantiatedSubTypeInfoWithLowerBound(
+ private InstantiatedSubTypeInfo instantiatedSubTypeInfoForInstantiatedType(
AppInfoWithLiveness appInfo,
DexProgramClass refinedReceiverUpperBound,
DexProgramClass refinedReceiverLowerBound,
Box<Boolean> hasInstantiatedLambdas) {
- return (type, subTypeConsumer, callSiteConsumer) -> {
- List<DexProgramClass> subTypes =
- appInfo.computeProgramClassRelationChain(
- refinedReceiverLowerBound, refinedReceiverUpperBound);
- for (DexProgramClass subType : subTypes) {
- if (appInfo.hasAnyInstantiatedLambdas(subType)) {
- hasInstantiatedLambdas.set(true);
- }
- subTypeConsumer.accept(subType);
+ return (ignored, subTypeConsumer, callSiteConsumer) -> {
+ Consumer<DexProgramClass> lambdaInstantiatedConsumer =
+ subType -> {
+ subTypeConsumer.accept(subType);
+ if (appInfo.hasAnyInstantiatedLambdas(subType)) {
+ hasInstantiatedLambdas.set(true);
+ }
+ };
+ if (refinedReceiverLowerBound == null) {
+ appInfo.forEachInstantiatedSubType(
+ refinedReceiverUpperBound.type, lambdaInstantiatedConsumer, callSiteConsumer);
+ } else {
+ appInfo.forEachInstantiatedSubTypeInChain(
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound,
+ lambdaInstantiatedConsumer,
+ callSiteConsumer);
}
};
}
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 663524d..dd2cde4 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -4,11 +4,9 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.Ordering;
@@ -150,28 +148,6 @@
return newType == appView.dexItemFactory().voidType;
}
- public boolean defaultValueHasChanged() {
- if (newType.isPrimitiveType()) {
- if (oldType.isPrimitiveType()) {
- return ValueType.fromDexType(newType) != ValueType.fromDexType(oldType);
- }
- return true;
- } else if (oldType.isPrimitiveType()) {
- return true;
- }
- // All reference types uses null as default value.
- assert newType.isReferenceType();
- assert oldType.isReferenceType();
- return false;
- }
-
- public TypeLatticeElement defaultValueLatticeElement(AppView<?> appView) {
- if (newType.isPrimitiveType()) {
- return TypeLatticeElement.fromDexType(newType, null, appView);
- }
- return TypeLatticeElement.getNull();
- }
-
@Override
public boolean isRewrittenTypeInfo() {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index f452556..2c27763 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -25,6 +25,8 @@
this.factory = factory;
}
+ public abstract boolean registerInitClass(DexType type);
+
public abstract boolean registerInvokeVirtual(DexMethod method);
public abstract boolean registerInvokeDirect(DexMethod method);
diff --git a/src/main/java/com/android/tools/r8/inspector/BooleanValueInspector.java b/src/main/java/com/android/tools/r8/inspector/BooleanValueInspector.java
new file mode 100644
index 0000000..1fba365
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/BooleanValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface BooleanValueInspector extends ValueInspector {
+ boolean getBooleanValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/ByteValueInspector.java b/src/main/java/com/android/tools/r8/inspector/ByteValueInspector.java
new file mode 100644
index 0000000..718183e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/ByteValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface ByteValueInspector extends ValueInspector {
+ byte getByteValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/CharValueInspector.java b/src/main/java/com/android/tools/r8/inspector/CharValueInspector.java
new file mode 100644
index 0000000..d79ee9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/CharValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface CharValueInspector extends ValueInspector {
+ char getCharValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/ClassInspector.java b/src/main/java/com/android/tools/r8/inspector/ClassInspector.java
new file mode 100644
index 0000000..b18dd26
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/ClassInspector.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import java.util.function.Consumer;
+
+/** Inspector for a class or interface definition. */
+@Keep
+public interface ClassInspector {
+
+ /** Get the class reference for the class of this inspector. */
+ ClassReference getClassReference();
+
+ /** Iterate all fields declared in the class/interface (unspecified order). */
+ void forEachField(Consumer<FieldInspector> inspection);
+
+ /** Iterate all methods declared in the class/interface (unspecified order). */
+ void forEachMethod(Consumer<MethodInspector> inspection);
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/DoubleValueInspector.java b/src/main/java/com/android/tools/r8/inspector/DoubleValueInspector.java
new file mode 100644
index 0000000..334f7ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/DoubleValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface DoubleValueInspector extends ValueInspector {
+ double getDoubleValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/FieldInspector.java b/src/main/java/com/android/tools/r8/inspector/FieldInspector.java
new file mode 100644
index 0000000..5d2e656
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/FieldInspector.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.FieldReference;
+import java.util.Optional;
+
+/** Inspector for a field definition. */
+@Keep
+public interface FieldInspector {
+
+ /** Get the field reference for the field of this inspector. */
+ FieldReference getFieldReference();
+
+ /** True if the field is declared static. */
+ boolean isStatic();
+
+ /** True if the field is declared final. */
+ boolean isFinal();
+
+ /**
+ * Returns an inspector for the initial value if it is known by the compiler.
+ *
+ * <p>Note that the determination of the value is best effort, often the value will simply be the
+ * default value for the given type.
+ */
+ Optional<ValueInspector> getInitialValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/FloatValueInspector.java b/src/main/java/com/android/tools/r8/inspector/FloatValueInspector.java
new file mode 100644
index 0000000..cb4c12d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/FloatValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface FloatValueInspector extends ValueInspector {
+ float getFloatValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/Inspector.java b/src/main/java/com/android/tools/r8/inspector/Inspector.java
new file mode 100644
index 0000000..880b450
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/Inspector.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+import com.android.tools.r8.Keep;
+import java.util.function.Consumer;
+
+/** Inspector providing access to various parts of an application. */
+@Keep
+public interface Inspector {
+
+ /** Iterate all classes and interfaces defined by the program (order unspecified). */
+ void forEachClass(Consumer<ClassInspector> inspection);
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/IntValueInspector.java b/src/main/java/com/android/tools/r8/inspector/IntValueInspector.java
new file mode 100644
index 0000000..38376c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/IntValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface IntValueInspector extends ValueInspector {
+ int getIntValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/LongValueInspector.java b/src/main/java/com/android/tools/r8/inspector/LongValueInspector.java
new file mode 100644
index 0000000..177a885
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/LongValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface LongValueInspector extends ValueInspector {
+ long getLongValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/MethodInspector.java b/src/main/java/com/android/tools/r8/inspector/MethodInspector.java
new file mode 100644
index 0000000..41cc8de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/MethodInspector.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.MethodReference;
+
+/** Inspector for a method definition. */
+@Keep
+public interface MethodInspector {
+
+ /** Get the method reference for the method of this inspector. */
+ MethodReference getMethodReference();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/ShortValueInspector.java b/src/main/java/com/android/tools/r8/inspector/ShortValueInspector.java
new file mode 100644
index 0000000..fb1d823
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/ShortValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface ShortValueInspector extends ValueInspector {
+ short getShortValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/StringValueInspector.java b/src/main/java/com/android/tools/r8/inspector/StringValueInspector.java
new file mode 100644
index 0000000..c0b78c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/StringValueInspector.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+public interface StringValueInspector extends ValueInspector {
+ String getStringValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/ValueInspector.java b/src/main/java/com/android/tools/r8/inspector/ValueInspector.java
new file mode 100644
index 0000000..84f1fe0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/ValueInspector.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.TypeReference;
+
+/** Inspector for a JVM representable value. */
+@Keep
+public interface ValueInspector {
+
+ /** Get the type reference describing the type of the value. */
+ TypeReference getTypeReference();
+
+ boolean isPrimitive();
+
+ boolean isBooleanValue();
+
+ boolean isByteValue();
+
+ boolean isCharValue();
+
+ boolean isShortValue();
+
+ boolean isIntValue();
+
+ boolean isLongValue();
+
+ boolean isFloatValue();
+
+ boolean isDoubleValue();
+
+ boolean isStringValue();
+
+ BooleanValueInspector asBooleanValue();
+
+ ByteValueInspector asByteValue();
+
+ CharValueInspector asCharValue();
+
+ ShortValueInspector asShortValue();
+
+ IntValueInspector asIntValue();
+
+ LongValueInspector asLongValue();
+
+ FloatValueInspector asFloatValue();
+
+ DoubleValueInspector asDoubleValue();
+
+ StringValueInspector asStringValue();
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java
new file mode 100644
index 0000000..c2ebc8e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/internal/ClassInspectorImpl.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector.internal;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.inspector.ClassInspector;
+import com.android.tools.r8.inspector.FieldInspector;
+import com.android.tools.r8.inspector.MethodInspector;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.util.function.Consumer;
+
+public class ClassInspectorImpl implements ClassInspector {
+
+ private final DexClass clazz;
+ private ClassReference reference = null;
+
+ ClassInspectorImpl(DexClass clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public ClassReference getClassReference() {
+ if (reference == null) {
+ reference = Reference.classFromDescriptor(clazz.type.toDescriptorString());
+ }
+ return reference;
+ }
+
+ @Override
+ public void forEachField(Consumer<FieldInspector> inspection) {
+ clazz.forEachField(field -> inspection.accept(new FieldInspectorImpl(this, field)));
+ }
+
+ @Override
+ public void forEachMethod(Consumer<MethodInspector> inspection) {
+ clazz.forEachMethod(method -> inspection.accept(new MethodInspectorImpl(this, method)));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/FieldInspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/FieldInspectorImpl.java
new file mode 100644
index 0000000..056d652e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/internal/FieldInspectorImpl.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector.internal;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.inspector.FieldInspector;
+import com.android.tools.r8.inspector.ValueInspector;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import java.util.Optional;
+
+public class FieldInspectorImpl implements FieldInspector {
+ private final ClassInspectorImpl parent;
+ private final DexEncodedField field;
+ private FieldReference reference = null;
+
+ public FieldInspectorImpl(ClassInspectorImpl parent, DexEncodedField field) {
+ this.parent = parent;
+ this.field = field;
+ }
+
+ @Override
+ public FieldReference getFieldReference() {
+ if (reference == null) {
+ reference =
+ Reference.field(
+ parent.getClassReference(),
+ field.field.name.toString(),
+ Reference.typeFromDescriptor(field.field.type.toDescriptorString()));
+ }
+ return reference;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return field.accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isFinal() {
+ return field.accessFlags.isFinal();
+ }
+
+ @Override
+ public Optional<ValueInspector> getInitialValue() {
+ if (field.isStatic() && field.getStaticValue() != null) {
+ return Optional.of(new ValueInspectorImpl(field.getStaticValue(), field.field.type));
+ }
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/InspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/InspectorImpl.java
new file mode 100644
index 0000000..c3d8a64
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/internal/InspectorImpl.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector.internal;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.inspector.ClassInspector;
+import com.android.tools.r8.inspector.Inspector;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class InspectorImpl implements Inspector {
+
+ // This wrapping appears odd, but allows hooking in inspections on the impl type from tests.
+ public static List<Consumer<InspectorImpl>> wrapInspections(
+ Collection<Consumer<Inspector>> inspections) {
+ if (inspections == null || inspections.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<Consumer<InspectorImpl>> wrapped = new ArrayList<>(inspections.size());
+ for (Consumer<Inspector> inspection : inspections) {
+ wrapped.add(inspection::accept);
+ }
+ return wrapped;
+ }
+
+ public static void runInspections(
+ List<Consumer<InspectorImpl>> inspections, DexApplication application) {
+ if (inspections == null || inspections.isEmpty()) {
+ return;
+ }
+ InspectorImpl inspector = new InspectorImpl(application);
+ for (Consumer<InspectorImpl> inspection : inspections) {
+ inspection.accept(inspector);
+ }
+ }
+
+ private final DexApplication application;
+
+ public InspectorImpl(DexApplication application) {
+ this.application = application;
+ }
+
+ @Override
+ public void forEachClass(Consumer<ClassInspector> inspection) {
+ for (DexProgramClass clazz : application.classes()) {
+ inspection.accept(new ClassInspectorImpl(clazz));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/MethodInspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/MethodInspectorImpl.java
new file mode 100644
index 0000000..1d868be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/internal/MethodInspectorImpl.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector.internal;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.inspector.MethodInspector;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.Arrays;
+
+public class MethodInspectorImpl implements MethodInspector {
+
+ private final ClassInspectorImpl parent;
+ private final DexEncodedMethod method;
+ private MethodReference reference;
+
+ public MethodInspectorImpl(ClassInspectorImpl parent, DexEncodedMethod method) {
+ this.parent = parent;
+ this.method = method;
+ }
+
+ @Override
+ public MethodReference getMethodReference() {
+ if (reference == null) {
+ reference =
+ Reference.method(
+ parent.getClassReference(),
+ method.method.name.toString(),
+ ListUtils.map(
+ Arrays.asList(method.method.proto.parameters.values),
+ param -> Reference.typeFromDescriptor(param.toDescriptorString())),
+ method.method.proto.returnType.isVoidType()
+ ? null
+ : Reference.typeFromDescriptor(
+ method.method.proto.returnType.toDescriptorString()));
+ }
+ return reference;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/inspector/internal/ValueInspectorImpl.java b/src/main/java/com/android/tools/r8/inspector/internal/ValueInspectorImpl.java
new file mode 100644
index 0000000..8f269d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/inspector/internal/ValueInspectorImpl.java
@@ -0,0 +1,198 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspector.internal;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.inspector.BooleanValueInspector;
+import com.android.tools.r8.inspector.ByteValueInspector;
+import com.android.tools.r8.inspector.CharValueInspector;
+import com.android.tools.r8.inspector.DoubleValueInspector;
+import com.android.tools.r8.inspector.FloatValueInspector;
+import com.android.tools.r8.inspector.IntValueInspector;
+import com.android.tools.r8.inspector.LongValueInspector;
+import com.android.tools.r8.inspector.ShortValueInspector;
+import com.android.tools.r8.inspector.StringValueInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+
+public class ValueInspectorImpl
+ implements BooleanValueInspector,
+ ByteValueInspector,
+ CharValueInspector,
+ ShortValueInspector,
+ IntValueInspector,
+ LongValueInspector,
+ FloatValueInspector,
+ DoubleValueInspector,
+ StringValueInspector {
+
+ private final DexValue value;
+ private final DexType type;
+
+ public ValueInspectorImpl(DexValue value, DexType type) {
+ this.value = value;
+ this.type = type;
+ }
+
+ @Override
+ public TypeReference getTypeReference() {
+ return Reference.typeFromDescriptor(type.toDescriptorString());
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return type.isPrimitiveType();
+ }
+
+ @Override
+ public boolean isBooleanValue() {
+ return type.isBooleanType();
+ }
+
+ @Override
+ public BooleanValueInspector asBooleanValue() {
+ return isBooleanValue() ? this : null;
+ }
+
+ @Override
+ public boolean getBooleanValue() {
+ guard(isBooleanValue());
+ return value.asDexValueBoolean().getValue();
+ }
+
+ @Override
+ public boolean isByteValue() {
+ return type.isByteType();
+ }
+
+ @Override
+ public ByteValueInspector asByteValue() {
+ return isByteValue() ? this : null;
+ }
+
+ @Override
+ public byte getByteValue() {
+ guard(isByteValue());
+ return value.asDexValueByte().getValue();
+ }
+
+ @Override
+ public boolean isCharValue() {
+ return type.isCharType();
+ }
+
+ @Override
+ public CharValueInspector asCharValue() {
+ return isCharValue() ? this : null;
+ }
+
+ @Override
+ public char getCharValue() {
+ guard(isCharValue());
+ return value.asDexValueChar().getValue();
+ }
+
+ @Override
+ public boolean isShortValue() {
+ return type.isShortType();
+ }
+
+ @Override
+ public ShortValueInspector asShortValue() {
+ return isShortValue() ? this : null;
+ }
+
+ @Override
+ public short getShortValue() {
+ guard(isShortValue());
+ return value.asDexValueShort().getValue();
+ }
+
+ @Override
+ public boolean isIntValue() {
+ return type.isIntType();
+ }
+
+ @Override
+ public IntValueInspector asIntValue() {
+ return isIntValue() ? this : null;
+ }
+
+ @Override
+ public int getIntValue() {
+ guard(isIntValue());
+ return value.asDexValueInt().value;
+ }
+
+ @Override
+ public boolean isLongValue() {
+ return type.isLongType();
+ }
+
+ @Override
+ public LongValueInspector asLongValue() {
+ return isLongValue() ? this : null;
+ }
+
+ @Override
+ public long getLongValue() {
+ guard(isLongValue());
+ return value.asDexValueLong().getValue();
+ }
+
+ @Override
+ public boolean isFloatValue() {
+ return type.isFloatType();
+ }
+
+ @Override
+ public FloatValueInspector asFloatValue() {
+ return isFloatValue() ? this : null;
+ }
+
+ @Override
+ public float getFloatValue() {
+ guard(isFloatValue());
+ return value.asDexValueFloat().getValue();
+ }
+
+ @Override
+ public boolean isDoubleValue() {
+ return type.isDoubleType();
+ }
+
+ @Override
+ public DoubleValueInspector asDoubleValue() {
+ return isDoubleValue() ? this : null;
+ }
+
+ @Override
+ public double getDoubleValue() {
+ guard(isDoubleValue());
+ return value.asDexValueDouble().getValue();
+ }
+
+ @Override
+ public boolean isStringValue() {
+ return type.isClassType() && value.isDexValueString();
+ }
+
+ @Override
+ public StringValueInspector asStringValue() {
+ return isStringValue() ? this : null;
+ }
+
+ @Override
+ public String getStringValue() {
+ guard(isStringValue());
+ return value.asDexValueString().getValue().toString();
+ }
+
+ private static void guard(boolean precondition) {
+ if (!precondition) {
+ throw new IllegalStateException("Invalid call on ValueInspector");
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 80c81e9..7270d42 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.code.DominatorTree.Inclusive;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
@@ -233,6 +234,20 @@
public static class InstructionUtils {
+ public static boolean forInitClass(
+ InitClass instruction,
+ DexType type,
+ AppView<?> appView,
+ Query mode,
+ AnalysisAssumption assumption) {
+ if (assumption == AnalysisAssumption.NONE) {
+ // Class initialization may fail with ExceptionInInitializerError.
+ return false;
+ }
+ DexClass clazz = appView.definitionFor(instruction.getClassValue());
+ return clazz != null && isTypeInitializedBy(instruction, type, clazz, appView, mode);
+ }
+
public static boolean forInstanceGet(
InstanceGet instruction,
DexType type,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
index b8e6950..0fc8bd0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -56,16 +56,17 @@
}
// isArrayPut and isFieldPut are missed as they don't have out value.
assert instr.isArgument()
- || instr.isAssume()
- || instr.isBinop()
- || instr.isUnop()
- || instr.isMonitor()
- || instr.isMove()
- || instr.isCheckCast()
- || instr.isInstanceOf()
- || instr.isConstInstruction()
- || instr.isJumpInstruction()
- || instr.isDebugInstruction()
+ || instr.isAssume()
+ || instr.isBinop()
+ || instr.isInitClass()
+ || instr.isUnop()
+ || instr.isMonitor()
+ || instr.isMove()
+ || instr.isCheckCast()
+ || instr.isInstanceOf()
+ || instr.isConstInstruction()
+ || instr.isJumpInstruction()
+ || instr.isDebugInstruction()
: "Instruction that impacts determinism: " + instr;
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index 72ac893..0bb8f21 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -117,7 +117,7 @@
block.deduplicatePhis();
}
- code.removeAllTrivialPhis();
+ code.removeAllDeadAndTrivialPhis();
}
private LatticeElement getLatticeElement(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index b06a015..5ddacd6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -5,17 +5,13 @@
package com.android.tools.r8.ir.analysis.fieldaccess;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.UseRegistry;
@@ -26,7 +22,6 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
@@ -61,7 +56,7 @@
assert feedback.noUpdatesLeft();
timing.begin("Compute fields of interest");
- computeFieldsOfInterest(appInfo);
+ computeFieldsOfInterest();
timing.end(); // Compute fields of interest
if (fieldsOfInterest.isEmpty()) {
@@ -81,40 +76,18 @@
fieldsOfInterest.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
}
- private void computeFieldsOfInterest(AppInfoWithLiveness appInfo) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- for (DexProgramClass clazz : appInfo.classes()) {
+ private void computeFieldsOfInterest() {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
for (DexEncodedField field : clazz.instanceFields()) {
if (canOptimizeField(field, appView)) {
fieldsOfInterest.add(field);
}
}
- OptionalBool mayRequireClinitField = OptionalBool.unknown();
- for (DexEncodedField field : clazz.staticFields()) {
- if (canOptimizeField(field, appView)) {
- if (mayRequireClinitField.isUnknown()) {
- mayRequireClinitField =
- OptionalBool.of(clazz.classInitializationMayHaveSideEffects(appView));
+ if (appView.canUseInitClass() || !clazz.classInitializationMayHaveSideEffects(appView)) {
+ for (DexEncodedField field : clazz.staticFields()) {
+ if (canOptimizeField(field, appView)) {
+ fieldsOfInterest.add(field);
}
- fieldsOfInterest.add(field);
- }
- }
- if (mayRequireClinitField.isTrue()) {
- DexField clinitField = dexItemFactory.objectMembers.clinitField;
- if (clazz.lookupStaticField(dexItemFactory.objectMembers.clinitField) == null) {
- FieldAccessFlags accessFlags =
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC
- | Constants.ACC_FINAL
- | Constants.ACC_PUBLIC
- | Constants.ACC_STATIC);
- clazz.appendStaticField(
- new DexEncodedField(
- dexItemFactory.createField(clazz.type, clinitField.type, clinitField.name),
- accessFlags,
- DexAnnotationSet.empty(),
- null));
- appView.appInfo().invalidateTypeCacheFor(clazz.type);
}
}
}
@@ -224,6 +197,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return false;
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index dffd9e0..5479737 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -111,7 +111,7 @@
public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
DexField rewrittenField = lens.lookupField(field);
assert !appView.unboxedEnums().containsEnum(field.holder)
- || !appView.definitionFor(rewrittenField).accessFlags.isEnum();
+ || !appView.appInfo().resolveField(rewrittenField).accessFlags.isEnum();
return appView.abstractValueFactory().createSingleFieldValue(rewrittenField);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 12ff943..da92bf3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -83,7 +83,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 8ab298a..589710f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -144,7 +144,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// In debug mode, ArrayPut has a side-effect on the locals.
if (appView.options().debug) {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index c28bc4a..a084a0f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -95,7 +95,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 1574275..a9089f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -135,7 +135,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
index f2cdc93..fdfbe61 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
@@ -160,6 +160,11 @@
}
@Override
+ public T visit(InitClass instruction) {
+ return null;
+ }
+
+ @Override
public T visit(InstanceGet instruction) {
return handleFieldInstruction(instruction);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 366f0cf..507ae25 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -28,20 +28,6 @@
public abstract class FieldInstruction extends Instruction {
- public enum Assumption {
- NONE,
- CLASS_ALREADY_INITIALIZED,
- RECEIVER_NOT_NULL;
-
- boolean canAssumeClassIsAlreadyInitialized() {
- return this == CLASS_ALREADY_INITIALIZED;
- }
-
- boolean canAssumeReceiverIsNotNull() {
- return this == RECEIVER_NOT_NULL;
- }
- }
-
private final DexField field;
protected FieldInstruction(DexField field, Value dest, Value value) {
@@ -76,11 +62,11 @@
@Override
public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
- return instructionInstanceCanThrow(appView, context, Assumption.NONE);
+ return instructionInstanceCanThrow(appView, context, SideEffectAssumption.NONE);
}
public AbstractError instructionInstanceCanThrow(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
DexEncodedField resolvedField;
if (appView.enableWholeProgramOptimizations()) {
// TODO(b/123857022): Should be possible to use definitionFor().
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 952bd5c..883d49f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1126,27 +1126,32 @@
return true;
}
- public boolean removeAllTrivialPhis() {
- return removeAllTrivialPhis(null, null);
+ public boolean removeAllDeadAndTrivialPhis() {
+ return removeAllDeadAndTrivialPhis(null, null);
}
- public boolean removeAllTrivialPhis(IRBuilder builder) {
- return removeAllTrivialPhis(builder, null);
+ public boolean removeAllDeadAndTrivialPhis(IRBuilder builder) {
+ return removeAllDeadAndTrivialPhis(builder, null);
}
- public boolean removeAllTrivialPhis(Set<Value> affectedValues) {
- return removeAllTrivialPhis(null, affectedValues);
+ public boolean removeAllDeadAndTrivialPhis(Set<Value> affectedValues) {
+ return removeAllDeadAndTrivialPhis(null, affectedValues);
}
- public boolean removeAllTrivialPhis(IRBuilder builder, Set<Value> affectedValues) {
- boolean anyTrivialPhisRemoved = false;
+ public boolean removeAllDeadAndTrivialPhis(IRBuilder builder, Set<Value> affectedValues) {
+ boolean anyPhisRemoved = false;
for (BasicBlock block : blocks) {
List<Phi> phis = new ArrayList<>(block.getPhis());
for (Phi phi : phis) {
- anyTrivialPhisRemoved |= phi.removeTrivialPhi(builder, affectedValues);
+ if (phi.hasAnyUsers()) {
+ anyPhisRemoved |= phi.removeTrivialPhi(builder, affectedValues);
+ } else {
+ phi.removeDeadPhi();
+ anyPhisRemoved = true;
+ }
}
}
- return anyTrivialPhisRemoved;
+ return anyPhisRemoved;
}
public int reserveMarkingColor() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
new file mode 100644
index 0000000..8dd260c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -0,0 +1,167 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isTypeVisibleFromContext;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.code.CfInitClass;
+import com.android.tools.r8.code.DexInitClass;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.AbstractError;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.google.common.collect.Sets;
+
+public class InitClass extends Instruction {
+
+ private final DexType clazz;
+
+ public InitClass(Value outValue, DexType clazz) {
+ super(outValue);
+ assert hasOutValue();
+ assert outValue.getTypeLattice().isInt();
+ assert clazz.isClassType();
+ this.clazz = clazz;
+ }
+
+ public DexType getClassValue() {
+ return clazz;
+ }
+
+ @Override
+ public boolean isInitClass() {
+ return true;
+ }
+
+ @Override
+ public InitClass asInitClass() {
+ return this;
+ }
+
+ @Override
+ public TypeLatticeElement evaluate(AppView<?> appView) {
+ return TypeLatticeElement.getInt();
+ }
+
+ @Override
+ public int opcode() {
+ return Opcodes.INIT_CLASS;
+ }
+
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(outValue(), getNumber());
+ builder.add(this, new DexInitClass(dest, clazz));
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInitClass(clazz));
+ }
+
+ @Override
+ public boolean definitelyTriggersClassInitialization(
+ DexType clazz,
+ DexType context,
+ AppView<?> appView,
+ Query mode,
+ AnalysisAssumption assumption) {
+ return ClassInitializationAnalysis.InstructionUtils.forInitClass(
+ this, clazz, appView, mode, assumption);
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return other.isInitClass() && clazz == other.asInitClass().clazz;
+ }
+
+ @Override
+ public AbstractError instructionInstanceCanThrow(AppView<?> appView, DexType context) {
+ if (!isTypeVisibleFromContext(appView, context, clazz)) {
+ return AbstractError.top();
+ }
+ if (clazz.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet())) {
+ return AbstractError.top();
+ }
+ return AbstractError.bottom();
+ }
+
+ @Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
+ return instructionInstanceCanThrow(appView, context).isThrowing();
+ }
+
+ @Override
+ public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, DexType context) {
+ if (appView.enableWholeProgramOptimizations()) {
+ // In R8, check if the class initialization of `clazz` or any of its ancestor types may have
+ // side effects.
+ return clazz.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
+ } else {
+ // In D8, this instruction may trigger class initialization if `clazz` is different from the
+ // current context.
+ return clazz != context;
+ }
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ return true;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, DexType invocationContext) {
+ return inliningConstraints.forInitClass(clazz, invocationContext);
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ helper.storeOutValue(this, it);
+ }
+
+ @Override
+ public boolean hasInvariantOutType() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; " + clazz.toSourceString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
index 94bfdaf..e78d3a7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
public interface InstanceFieldInstruction {
@@ -16,10 +16,13 @@
Value object();
- boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+ boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption);
FieldInstruction asFieldInstruction();
+ boolean isInstanceFieldInstruction();
+
boolean isInstanceGet();
InstanceGet asInstanceGet();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index b070660..6d9b8e9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -115,13 +115,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
}
@@ -161,6 +156,16 @@
}
@Override
+ public boolean isInstanceFieldInstruction() {
+ return true;
+ }
+
+ @Override
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return this;
+ }
+
+ @Override
public boolean isInstanceGet() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 00a5f30..5680260 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -114,13 +114,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
@@ -200,6 +195,16 @@
}
@Override
+ public boolean isInstanceFieldInstruction() {
+ return true;
+ }
+
+ @Override
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return this;
+ }
+
+ @Override
public boolean isInstancePut() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index b1459be..a56f0a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -571,6 +571,11 @@
}
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
+ }
+
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow();
}
@@ -888,6 +893,14 @@
return null;
}
+ public boolean isInstanceFieldInstruction() {
+ return false;
+ }
+
+ public InstanceFieldInstruction asInstanceFieldInstruction() {
+ return null;
+ }
+
public boolean isInstanceGet() {
return false;
}
@@ -1032,6 +1045,14 @@
return null;
}
+ public boolean isInitClass() {
+ return false;
+ }
+
+ public InitClass asInitClass() {
+ return null;
+ }
+
public boolean isAdd() {
return false;
}
@@ -1452,4 +1473,18 @@
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return false;
}
+
+ public enum SideEffectAssumption {
+ NONE,
+ CLASS_ALREADY_INITIALIZED,
+ RECEIVER_NOT_NULL;
+
+ boolean canAssumeClassIsAlreadyInitialized() {
+ return this == CLASS_ALREADY_INITIALIZED;
+ }
+
+ boolean canAssumeReceiverIsNotNull() {
+ return this == RECEIVER_NOT_NULL;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
index d7b3a83..4bb29bb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -64,6 +64,8 @@
T visit(Inc instruction);
+ T visit(InitClass instruction);
+
T visit(InstanceGet instruction);
T visit(InstanceOf instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ec0c68f..b4531c3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -158,14 +158,15 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.options().debug) {
return true;
}
// Check if it could throw a NullPointerException as a result of the receiver being null.
Value receiver = getReceiver();
- if (receiver.getTypeLattice().isNullable()) {
+ if (!assumption.canAssumeReceiverIsNotNull() && receiver.getTypeLattice().isNullable()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 831c65f..02e45ef 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -171,7 +171,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Check if the instruction has a side effect on the locals environment.
if (hasOutValue() && outValue().hasLocalInfo()) {
assert appView.options().debug;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 8378a5d..ce75c7c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -184,7 +184,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Check if the instruction has a side effect on the locals environment.
if (hasOutValue() && outValue().hasLocalInfo()) {
assert appView.options().debug;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index da1c8fd..2d29972 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -156,7 +156,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (!appView.enableWholeProgramOptimizations()) {
return true;
}
@@ -187,22 +188,24 @@
}
// Verify that the target method does not have side-effects.
- boolean targetMayHaveSideEffects;
if (appViewWithLiveness.appInfo().noSideEffects.containsKey(target.method)) {
- targetMayHaveSideEffects = false;
- } else {
- targetMayHaveSideEffects =
- target.getOptimizationInfo().mayHaveSideEffects()
- // Verify that calling the target method won't lead to class initialization.
- || target.method.holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appView.isSubtype(context, type).isTrue(),
- Sets.newIdentityHashSet());
+ return false;
}
- return targetMayHaveSideEffects;
+ if (target.getOptimizationInfo().mayHaveSideEffects()) {
+ return true;
+ }
+
+ if (assumption.canAssumeClassIsAlreadyInitialized()) {
+ return false;
+ }
+
+ return target.method.holder.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized
+ // already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 1ec8873..1218f08 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -142,7 +142,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (!appView.enableWholeProgramOptimizations()) {
return true;
}
@@ -153,7 +154,7 @@
// Check if it could throw a NullPointerException as a result of the receiver being null.
Value receiver = getReceiver();
- if (receiver.getTypeLattice().isNullable()) {
+ if (!assumption.canAssumeReceiverIsNotNull() && receiver.getTypeLattice().isNullable()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 226b771..afd87ff 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -129,7 +129,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
// Treat the instruction as possibly having side-effects if it may throw or the array is used.
if (instructionInstanceCanThrow(appView, context).isThrowing()
|| src().numberOfAllUsers() > 1) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 23ac981..384a27a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -141,7 +141,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (!appView.enableWholeProgramOptimizations()) {
return !(dexItemFactory.libraryTypesAssumedToBePresent.contains(clazz)
diff --git a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
index 65a09d6..a3dc92b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
@@ -33,43 +33,44 @@
int GOTO = 24;
int IF = 25;
int INC = 26;
- int INSTANCE_GET = 27;
- int INSTANCE_OF = 28;
- int INSTANCE_PUT = 29;
- int INT_SWITCH = 30;
- int INVOKE_CUSTOM = 31;
- int INVOKE_DIRECT = 32;
- int INVOKE_INTERFACE = 33;
- int INVOKE_MULTI_NEW_ARRAY = 34;
- int INVOKE_NEW_ARRAY = 35;
- int INVOKE_POLYMORPHIC = 36;
- int INVOKE_STATIC = 37;
- int INVOKE_SUPER = 38;
- int INVOKE_VIRTUAL = 39;
- int LOAD = 40;
- int MONITOR = 41;
- int MOVE = 42;
- int MOVE_EXCEPTION = 43;
- int MUL = 44;
- int NEG = 45;
- int NEW_ARRAY_EMPTY = 46;
- int NEW_ARRAY_FILLED_DATA = 47;
- int NEW_INSTANCE = 48;
- int NOT = 49;
- int NUMBER_CONVERSION = 50;
- int OR = 51;
- int POP = 52;
- int REM = 53;
- int RETURN = 54;
- int SHL = 55;
- int SHR = 56;
- int STATIC_GET = 57;
- int STATIC_PUT = 58;
- int STORE = 59;
- int STRING_SWITCH = 60;
- int SUB = 61;
- int SWAP = 62;
- int THROW = 63;
- int USHR = 64;
- int XOR = 65;
+ int INIT_CLASS = 27;
+ int INSTANCE_GET = 28;
+ int INSTANCE_OF = 29;
+ int INSTANCE_PUT = 30;
+ int INT_SWITCH = 31;
+ int INVOKE_CUSTOM = 32;
+ int INVOKE_DIRECT = 33;
+ int INVOKE_INTERFACE = 34;
+ int INVOKE_MULTI_NEW_ARRAY = 35;
+ int INVOKE_NEW_ARRAY = 36;
+ int INVOKE_POLYMORPHIC = 37;
+ int INVOKE_STATIC = 38;
+ int INVOKE_SUPER = 39;
+ int INVOKE_VIRTUAL = 40;
+ int LOAD = 41;
+ int MONITOR = 42;
+ int MOVE = 43;
+ int MOVE_EXCEPTION = 44;
+ int MUL = 45;
+ int NEG = 46;
+ int NEW_ARRAY_EMPTY = 47;
+ int NEW_ARRAY_FILLED_DATA = 48;
+ int NEW_INSTANCE = 49;
+ int NOT = 50;
+ int NUMBER_CONVERSION = 51;
+ int OR = 52;
+ int POP = 53;
+ int REM = 54;
+ int RETURN = 55;
+ int SHL = 56;
+ int SHR = 57;
+ int STATIC_GET = 58;
+ int STATIC_PUT = 59;
+ int STORE = 60;
+ int STRING_SWITCH = 61;
+ int SUB = 62;
+ int SWAP = 63;
+ int THROW = 64;
+ int USHR = 65;
+ int XOR = 66;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
index 9c5a123..286b470 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
public interface StaticFieldInstruction {
@@ -14,7 +14,10 @@
Value outValue();
- boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+ boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context);
+
+ boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, SideEffectAssumption assumption);
FieldInstruction asFieldInstruction();
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index e7c2dee..a3c202b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -137,12 +137,12 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ return instructionMayHaveSideEffects(appView, context, SideEffectAssumption.NONE);
}
@Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 9d25691..e7d2bdc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -94,13 +94,8 @@
}
@Override
- public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
- return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
- }
-
- @Override
public boolean instructionMayHaveSideEffects(
- AppView<?> appView, DexType context, Assumption assumption) {
+ AppView<?> appView, DexType context, SideEffectAssumption assumption) {
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 25ddb6a..6ff7e2f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -282,6 +282,21 @@
}
}
+ private void processInitClass(DexType type) {
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
+ if (clazz == null) {
+ assert false;
+ return;
+ }
+ addClassInitializerTarget(clazz);
+ }
+
+ @Override
+ public boolean registerInitClass(DexType clazz) {
+ processInitClass(clazz);
+ return false;
+ }
+
@Override
public boolean registerInvokeVirtual(DexMethod method) {
processInvoke(Invoke.Type.VIRTUAL, method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 3c33035..e87f619 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -71,6 +71,7 @@
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ImpreciseMemberTypeInstruction;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
@@ -687,7 +688,7 @@
block.deduplicatePhis();
}
- ir.removeAllTrivialPhis(this);
+ ir.removeAllDeadAndTrivialPhis(this);
ir.removeUnreachableBlocks();
// Compute precise types for all values.
@@ -1795,6 +1796,13 @@
closeCurrentBlock(ret);
}
+ public void addInitClass(int dest, DexType clazz) {
+ Value out = writeRegister(dest, TypeLatticeElement.getInt(), ThrowingInfo.CAN_THROW);
+ InitClass instruction = new InitClass(out, clazz);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
public void addStaticGet(int dest, DexField field) {
Value out =
writeRegister(
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 f4e315b..d968d31 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
@@ -395,11 +395,10 @@
throw new Unreachable();
}
- private boolean removeLambdaDeserializationMethods() {
+ private void removeLambdaDeserializationMethods() {
if (lambdaRewriter != null) {
- return lambdaRewriter.removeLambdaDeserializationMethods(appView.appInfo().classes());
+ lambdaRewriter.removeLambdaDeserializationMethods(appView.appInfo().classes());
}
- return false;
}
private void desugarNestBasedAccess(Builder<?> builder, ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index d7cfc6b..06c4bdf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstMethodHandle;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
@@ -61,6 +62,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.logging.Log;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -138,6 +140,11 @@
affectedPhis.addAll(newOutValue.uniquePhiUsers());
}
}
+ } else if (current.isInitClass()) {
+ InitClass initClass = current.asInitClass();
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ initClass.getClassValue(), (t, v) -> new InitClass(v, t));
} else if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
@@ -195,22 +202,14 @@
ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i);
if (argumentInfo.isRewrittenTypeInfo()) {
RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo();
- Value value = invoke.inValues().get(i);
- // When converting types the default value may change (for example default value
- // of a reference type is null while default value of int is 0).
- if (argInfo.defaultValueHasChanged()
- && value.isConstNumber()
- && value.definition.asConstNumber().isZero()) {
- iterator.previous();
- // TODO(b/150188380): Add API to insert a const instruction with a type lattice.
- Value rewrittenNull =
- iterator.insertConstIntInstruction(code, appView.options(), 0);
- iterator.next();
- rewrittenNull.setTypeLattice(argInfo.defaultValueLatticeElement(appView));
- newInValues.add(rewrittenNull);
- } else {
- newInValues.add(invoke.inValues().get(i));
- }
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code,
+ iterator,
+ argInfo.getOldType(),
+ argInfo.getNewType(),
+ invoke.inValues().get(i));
+ newInValues.add(rewrittenValue);
} else if (!argumentInfo.isRemovedArgumentInfo()) {
newInValues.add(invoke.inValues().get(i));
}
@@ -311,9 +310,12 @@
iterator.replaceCurrentInstruction(
new InvokeStatic(replacementMethod, null, current.inValues()));
} else if (actualField != field) {
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code, iterator, field.type, actualField.type, instancePut.value());
InstancePut newInstancePut =
InstancePut.createPotentiallyInvalid(
- actualField, instancePut.object(), instancePut.value());
+ actualField, instancePut.object(), rewrittenValue);
iterator.replaceCurrentInstruction(newInstancePut);
}
} else if (current.isStaticGet()) {
@@ -348,7 +350,10 @@
iterator.replaceCurrentInstruction(
new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
} else if (actualField != field) {
- StaticPut newStaticPut = new StaticPut(staticPut.value(), actualField);
+ Value rewrittenValue =
+ rewriteValueIfDefault(
+ code, iterator, field.type, actualField.type, staticPut.value());
+ StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField);
iterator.replaceCurrentInstruction(newStaticPut);
}
} else if (current.isCheckCast()) {
@@ -414,6 +419,49 @@
assert code.hasNoVerticallyMergedClasses(appView);
}
+ // If the initialValue is a default value and its type is rewritten from a reference type to a
+ // primitive type, then the default value type lattice needs to be changed.
+ private Value rewriteValueIfDefault(
+ IRCode code,
+ InstructionListIterator iterator,
+ DexType oldType,
+ DexType newType,
+ Value initialValue) {
+ if (initialValue.isConstNumber()
+ && initialValue.definition.asConstNumber().isZero()
+ && defaultValueHasChanged(oldType, newType)) {
+ iterator.previous();
+ // TODO(b/150188380): Add API to insert a const instruction with a type lattice.
+ Value rewrittenDefaultValue = iterator.insertConstIntInstruction(code, appView.options(), 0);
+ iterator.next();
+ rewrittenDefaultValue.setTypeLattice(defaultValueLatticeElement(newType));
+ return rewrittenDefaultValue;
+ }
+ return initialValue;
+ }
+
+ private boolean defaultValueHasChanged(DexType oldType, DexType newType) {
+ if (newType.isPrimitiveType()) {
+ if (oldType.isPrimitiveType()) {
+ return ValueType.fromDexType(newType) != ValueType.fromDexType(oldType);
+ }
+ return true;
+ } else if (oldType.isPrimitiveType()) {
+ return true;
+ }
+ // All reference types uses null as default value.
+ assert newType.isReferenceType();
+ assert oldType.isReferenceType();
+ return false;
+ }
+
+ private TypeLatticeElement defaultValueLatticeElement(DexType type) {
+ if (type.isPrimitiveType()) {
+ return TypeLatticeElement.fromDexType(type, null, appView);
+ }
+ return TypeLatticeElement.getNull();
+ }
+
public DexCallSite rewriteCallSite(DexCallSite callSite, DexEncodedMethod context) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto newMethodProto =
@@ -643,6 +691,7 @@
} else {
assert current.hasInvariantOutType();
assert current.isConstClass()
+ || current.isInitClass()
|| current.isInstanceOf()
|| (current.isInvokeVirtual()
&& current.asInvokeVirtual().getInvokedMethod().holder.isArrayType());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
index 41d1363..20ab05c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
@@ -113,7 +113,7 @@
}
}
if (changed) {
- code.removeAllTrivialPhis();
+ code.removeAllDeadAndTrivialPhis();
code.removeUnreachableBlocks();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
index e4af36c..7b53a35 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -84,7 +84,6 @@
new InvokeStatic(bridge, invokeMethod.outValue(), invokeMethod.arguments()));
}
}
-
} else if (instruction.isFieldInstruction()) {
DexEncodedField encodedField =
appView.definitionFor(instruction.asFieldInstruction().getField());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 0831e6d..ec1b05f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -615,39 +615,41 @@
DexMethod implMethod = descriptor.implHandle.asMethod();
DexClass implMethodHolder = definitionFor(implMethod.holder);
- List<DexEncodedMethod> directMethods = implMethodHolder.directMethods();
- for (int i = 0; i < directMethods.size(); i++) {
- DexEncodedMethod encodedMethod = directMethods.get(i);
- if (implMethod.match(encodedMethod)) {
- // We need to create a new static method with the same code to be able to safely
- // relax its accessibility without making it virtual.
- MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
- newAccessFlags.setStatic();
- newAccessFlags.unsetPrivate();
- // Always make the method public to provide access when r8 minification is allowed to move
- // the lambda class accessing this method to another package (-allowaccessmodification).
- newAccessFlags.setPublic();
- DexEncodedMethod newMethod =
- new DexEncodedMethod(
- callTarget,
- newAccessFlags,
- encodedMethod.annotations(),
- encodedMethod.parameterAnnotationsList,
- encodedMethod.getCode(),
- true);
- newMethod.copyMetadata(encodedMethod);
- rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method);
+ DexEncodedMethod replacement =
+ implMethodHolder
+ .getMethodCollection()
+ .replaceDirectMethod(
+ implMethod,
+ encodedMethod -> {
+ // We need to create a new static method with the same code to be able to safely
+ // relax its accessibility without making it virtual.
+ MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
+ newAccessFlags.setStatic();
+ newAccessFlags.unsetPrivate();
+ // Always make the method public to provide access when r8 minification is
+ // allowed to move the lambda class accessing this method to another package
+ // (-allowaccessmodification).
+ newAccessFlags.setPublic();
+ DexEncodedMethod newMethod =
+ new DexEncodedMethod(
+ callTarget,
+ newAccessFlags,
+ encodedMethod.annotations(),
+ encodedMethod.parameterAnnotationsList,
+ encodedMethod.getCode(),
+ true);
+ newMethod.copyMetadata(encodedMethod);
+ rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method);
- DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
- implMethodHolder.setDirectMethod(i, newMethod);
+ DexEncodedMethod.setDebugInfoWithFakeThisParameter(
+ newMethod.getCode(), callTarget.getArity(), rewriter.getAppView());
+ return newMethod;
+ });
- return newMethod;
- }
- }
- assert false
+ assert replacement != null
: "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName();
- return null;
+
+ return replacement;
}
}
@@ -673,33 +675,29 @@
private DexEncodedMethod modifyLambdaImplementationMethod(
DexMethod implMethod, DexClass implMethodHolder) {
- List<DexEncodedMethod> oldDirectMethods = implMethodHolder.directMethods();
- for (int i = 0; i < oldDirectMethods.size(); i++) {
- DexEncodedMethod encodedMethod = oldDirectMethods.get(i);
- if (implMethod.match(encodedMethod)) {
- // We need to create a new method with the same code to be able to safely relax its
- // accessibility and make it virtual.
- MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
- newAccessFlags.unsetPrivate();
- newAccessFlags.setPublic();
- DexEncodedMethod newMethod =
- new DexEncodedMethod(
- callTarget,
- newAccessFlags,
- encodedMethod.annotations(),
- encodedMethod.parameterAnnotationsList,
- encodedMethod.getCode(),
- true);
- newMethod.copyMetadata(encodedMethod);
- rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method);
- // Move the method from the direct methods to the virtual methods set.
- implMethodHolder.removeDirectMethod(i);
- implMethodHolder.appendVirtualMethod(newMethod);
-
- return newMethod;
- }
- }
- return null;
+ return implMethodHolder
+ .getMethodCollection()
+ .replaceDirectMethodWithVirtualMethod(
+ implMethod,
+ encodedMethod -> {
+ assert encodedMethod.isDirectMethod();
+ // We need to create a new method with the same code to be able to safely relax its
+ // accessibility and make it virtual.
+ MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
+ newAccessFlags.unsetPrivate();
+ newAccessFlags.setPublic();
+ DexEncodedMethod newMethod =
+ new DexEncodedMethod(
+ callTarget,
+ newAccessFlags,
+ encodedMethod.annotations(),
+ encodedMethod.parameterAnnotationsList,
+ encodedMethod.getCode(),
+ true);
+ newMethod.copyMetadata(encodedMethod);
+ rewriter.originalMethodSignatures.put(callTarget, encodedMethod.method);
+ return newMethod;
+ });
}
private DexEncodedMethod createSyntheticAccessor(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 007f170..770f170 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -172,29 +172,10 @@
}
/** Remove lambda deserialization methods. */
- public boolean removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
- boolean anyRemoved = false;
+ public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
for (DexProgramClass clazz : classes) {
- // Search for a lambda deserialization method and remove it if found.
- List<DexEncodedMethod> directMethods = clazz.directMethods();
- if (directMethods != null) {
- int methodCount = directMethods.size();
- for (int i = 0; i < methodCount; i++) {
- DexEncodedMethod encoded = directMethods.get(i);
- DexMethod method = encoded.method;
- if (method.isLambdaDeserializeMethod(getFactory())) {
- assert encoded.accessFlags.isStatic();
- assert encoded.accessFlags.isSynthetic();
- clazz.removeDirectMethod(i);
- anyRemoved = true;
-
- // We assume there is only one such method in the class.
- break;
- }
- }
- }
+ clazz.removeDirectMethod(getFactory().deserializeLambdaMethod);
}
- return anyRemoved;
}
/** Generates lambda classes and adds them to the builder. */
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 4c2a36d..b78757d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -375,6 +375,12 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ // Nothing to do since we always use a public field for initializing the class.
+ return true;
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
// Calls to class nest mate private methods are targeted by invokeVirtual in jdk11.
// The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
index 0ebe028..6bb9b03 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
@@ -55,9 +55,6 @@
public void markUsersForRemoval(Value value) {
for (Instruction user : value.aliasedUsers()) {
if (user.isAssumeDynamicType()) {
- assert value.numberOfAllUsers() == 1
- : "Expected value flowing into Assume<DynamicTypeAssumption> instruction to have a "
- + "unique user.";
markForRemoval(user.asAssumeDynamicType());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 60813ab..f7755d4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.analysis.type.TypeUtils;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.AlwaysMaterializingNop;
import com.android.tools.r8.ir.code.ArrayLength;
@@ -208,7 +207,7 @@
// Therefore, Assume elimination may result in a trivial phi:
// z <- phi(x, x)
if (needToCheckTrivialPhis) {
- code.removeAllTrivialPhis(valuesThatRequireWidening);
+ code.removeAllDeadAndTrivialPhis(valuesThatRequireWidening);
}
if (!valuesThatRequireWidening.isEmpty()) {
@@ -273,9 +272,34 @@
}
Throw throwInstruction = valueIsNullTarget.exit().asThrow();
- Value exceptionValue = throwInstruction.exception();
- if (!exceptionValue.isConstZero()
- && !TypeUtils.isNullPointerException(exceptionValue.getTypeLattice(), appView)) {
+ Value exceptionValue = throwInstruction.exception().getAliasedValue();
+ Value message;
+ if (exceptionValue.isConstZero()) {
+ message = null;
+ } else if (exceptionValue.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ NewInstance newInstance = exceptionValue.definition.asNewInstance();
+ if (newInstance.clazz != dexItemFactory.npeType) {
+ continue;
+ }
+ if (newInstance.outValue().numberOfAllUsers() != 2) {
+ continue; // Could be mutated before it is thrown.
+ }
+ InvokeDirect constructorCall = newInstance.getUniqueConstructorInvoke(dexItemFactory);
+ if (constructorCall == null) {
+ continue;
+ }
+ DexMethod invokedMethod = constructorCall.getInvokedMethod();
+ if (invokedMethod == dexItemFactory.npeMethods.init) {
+ message = null;
+ } else if (invokedMethod == dexItemFactory.npeMethods.initWithMessage) {
+ if (!appView.options().canUseRequireNonNull()) {
+ continue;
+ }
+ message = constructorCall.getArgument(1);
+ } else {
+ continue;
+ }
+ } else {
continue;
}
@@ -290,12 +314,26 @@
continue;
}
+ if (message != null) {
+ Instruction definition = message.definition;
+ if (message.definition.getBlock() == valueIsNullTarget) {
+ it.previous();
+ Instruction entry;
+ do {
+ entry = valueIsNullTarget.getInstructions().removeFirst();
+ it.add(entry);
+ } while (entry != definition);
+ it.next();
+ }
+ }
+
rewriteIfToRequireNonNull(
block,
it,
ifInstruction,
ifInstruction.targetFromCondition(1),
valueIsNullTarget,
+ message,
throwInstruction.getPosition());
shouldRemoveUnreachableBlocks = true;
}
@@ -1221,10 +1259,10 @@
assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
if (!blocksToBeRemoved.isEmpty()) {
code.removeBlocks(blocksToBeRemoved);
- code.removeAllTrivialPhis(affectedValues);
+ code.removeAllDeadAndTrivialPhis(affectedValues);
assert code.getUnreachableBlocks().isEmpty();
} else if (mayHaveRemovedTrivialPhi || assumeDynamicTypeRemover.mayHaveIntroducedTrivialPhi()) {
- code.removeAllTrivialPhis(affectedValues);
+ code.removeAllDeadAndTrivialPhis(affectedValues);
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
@@ -1299,7 +1337,7 @@
// Removing check-cast may result in a trivial phi:
// v3 <- phi(v1, v1)
if (needToRemoveTrivialPhis) {
- code.removeAllTrivialPhis(affectedValues);
+ code.removeAllDeadAndTrivialPhis(affectedValues);
if (!affectedValues.isEmpty()) {
typeAnalysis.narrowing(affectedValues);
}
@@ -2593,7 +2631,7 @@
}
if (changed) {
- code.removeAllTrivialPhis();
+ code.removeAllDeadAndTrivialPhis();
}
assert code.isConsistentSSA();
}
@@ -2922,15 +2960,24 @@
If theIf,
BasicBlock target,
BasicBlock deadTarget,
+ Value message,
Position position) {
deadTarget.unlinkSinglePredecessorSiblingsAllowed();
assert theIf == block.exit();
iterator.previous();
Instruction instruction;
if (appView.options().canUseRequireNonNull()) {
- DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
- instruction = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs()));
+ if (message != null) {
+ DexMethod requireNonNullMethod =
+ appView.dexItemFactory().objectsMethods.requireNonNullWithMessage;
+ instruction =
+ new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs(), message));
+ } else {
+ DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
+ instruction = new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(theIf.lhs()));
+ }
} else {
+ assert message == null;
DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
instruction = new InvokeVirtual(getClassMethod, null, ImmutableList.of(theIf.lhs()));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 5160e6c..2dff525 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -257,7 +257,7 @@
shouldSimplifyIfs |= newConst.outValue().hasUserThatMatches(Instruction::isIf);
} while (iterator.hasNext());
- shouldSimplifyIfs |= code.removeAllTrivialPhis();
+ shouldSimplifyIfs |= code.removeAllDeadAndTrivialPhis();
if (shouldSimplifyIfs) {
codeRewriter.simplifyIf(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index f1c5dc9..463bfce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -128,62 +128,6 @@
return false;
}
- private boolean canInlineStaticInvoke(
- InvokeStatic invoke,
- DexEncodedMethod method,
- DexEncodedMethod target,
- ClassInitializationAnalysis classInitializationAnalysis,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- // Only proceed with inlining a static invoke if:
- // - the holder for the target is a subtype of the holder for the method,
- // - the target method always triggers class initialization of its holder before any other side
- // effect (hence preserving class initialization semantics),
- // - the current method has already triggered the holder for the target method to be
- // initialized, or
- // - there is no non-trivial class initializer.
- DexType targetHolder = target.method.holder;
- if (appView.appInfo().isSubtype(method.method.holder, targetHolder)) {
- return true;
- }
- DexClass clazz = appView.definitionFor(targetHolder);
- assert clazz != null;
- if (target.getOptimizationInfo().triggersClassInitBeforeAnySideEffect()) {
- return true;
- }
- if (!method.isStatic()) {
- boolean targetIsGuaranteedToBeInitialized =
- appView.withInitializedClassesInInstanceMethods(
- analysis ->
- analysis.isClassDefinitelyLoadedInInstanceMethodsOn(
- target.method.holder, method.method.holder),
- false);
- if (targetIsGuaranteedToBeInitialized) {
- return true;
- }
- }
- if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction(
- target.method.holder, invoke)) {
- return true;
- }
- // Check for class initializer side effects when loading this class, as inlining might remove
- // the load operation.
- //
- // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-5.html#jvms-5.5.
- //
- // For simplicity, we are conservative and consider all interfaces, not only the ones with
- // default methods.
- if (!clazz.classInitializationMayHaveSideEffects(appView)) {
- return true;
- }
-
- if (appView.rootSet().bypassClinitForInlining.contains(target.method)) {
- return true;
- }
-
- whyAreYouNotInliningReporter.reportMustTriggerClassInitialization();
- return false;
- }
-
@Override
public boolean passesInliningConstraints(
InvokeMethod invoke,
@@ -391,12 +335,71 @@
Reason reason,
ClassInitializationAnalysis classInitializationAnalysis,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- // Abort inlining attempt if we can not guarantee class for static target has been initialized.
- if (!canInlineStaticInvoke(
- invoke, method, singleTarget, classInitializationAnalysis, whyAreYouNotInliningReporter)) {
- return null;
+ InlineAction action = new InlineAction(singleTarget, invoke, reason);
+ if (isTargetClassInitialized(invoke, method, singleTarget, classInitializationAnalysis)) {
+ return action;
}
- return new InlineAction(singleTarget, invoke, reason);
+ if (appView.canUseInitClass()
+ && appView.options().enableInliningOfInvokesWithClassInitializationSideEffects) {
+ action.setShouldSynthesizeInitClass();
+ return action;
+ }
+ whyAreYouNotInliningReporter.reportMustTriggerClassInitialization();
+ return null;
+ }
+
+ private boolean isTargetClassInitialized(
+ InvokeStatic invoke,
+ DexEncodedMethod method,
+ DexEncodedMethod target,
+ ClassInitializationAnalysis classInitializationAnalysis) {
+ // Only proceed with inlining a static invoke if:
+ // - the holder for the target is a subtype of the holder for the method,
+ // - the target method always triggers class initialization of its holder before any other side
+ // effect (hence preserving class initialization semantics),
+ // - the current method has already triggered the holder for the target method to be
+ // initialized, or
+ // - there is no non-trivial class initializer.
+ DexType targetHolder = target.method.holder;
+ if (appView.appInfo().isSubtype(method.method.holder, targetHolder)) {
+ return true;
+ }
+ DexClass clazz = appView.definitionFor(targetHolder);
+ assert clazz != null;
+ if (target.getOptimizationInfo().triggersClassInitBeforeAnySideEffect()) {
+ return true;
+ }
+ if (!method.isStatic()) {
+ boolean targetIsGuaranteedToBeInitialized =
+ appView.withInitializedClassesInInstanceMethods(
+ analysis ->
+ analysis.isClassDefinitelyLoadedInInstanceMethodsOn(
+ target.method.holder, method.method.holder),
+ false);
+ if (targetIsGuaranteedToBeInitialized) {
+ return true;
+ }
+ }
+ if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction(
+ target.method.holder, invoke)) {
+ return true;
+ }
+ // Check for class initializer side effects when loading this class, as inlining might remove
+ // the load operation.
+ //
+ // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-5.html#jvms-5.5.
+ //
+ // For simplicity, we are conservative and consider all interfaces, not only the ones with
+ // default methods.
+ if (!clazz.classInitializationMayHaveSideEffects(appView)) {
+ return true;
+ }
+
+ if (appView.rootSet().bypassClinitForInlining.contains(target.method)) {
+ return true;
+ }
+
+ return false;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 9cbc694..ebf6e57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -248,7 +248,7 @@
}
}
- code.removeAllTrivialPhis();
+ code.removeAllDeadAndTrivialPhis();
assert code.isConsistentSSA();
}
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 c9bf058..d45bbb0 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
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -573,6 +574,7 @@
public final Invoke invoke;
final Reason reason;
+ private boolean shouldSynthesizeInitClass;
private boolean shouldSynthesizeNullCheckForReceiver;
InlineAction(DexEncodedMethod target, Invoke invoke, Reason reason) {
@@ -581,7 +583,13 @@
this.reason = reason;
}
+ void setShouldSynthesizeInitClass() {
+ assert !shouldSynthesizeNullCheckForReceiver;
+ shouldSynthesizeInitClass = true;
+ }
+
void setShouldSynthesizeNullCheckForReceiver() {
+ assert !shouldSynthesizeInitClass;
shouldSynthesizeNullCheckForReceiver = true;
}
@@ -599,6 +607,12 @@
// Build the IR for a yet not processed method, and perform minimal IR processing.
IRCode code = inliningIRProvider.getInliningIR(invoke, target);
+ // Insert a init class instruction if this is needed to preserve class initialization
+ // semantics.
+ if (shouldSynthesizeInitClass) {
+ synthesizeInitClass(code);
+ }
+
// Insert a null check if this is needed to preserve the implicit null check for the receiver.
// This is only needed if we do not also insert a monitor-enter instruction, since that will
// throw a NPE if the receiver is null.
@@ -737,6 +751,21 @@
return new InlineeWithReason(code, reason);
}
+ private void synthesizeInitClass(IRCode code) {
+ List<Value> arguments = code.collectArguments();
+ BasicBlock entryBlock = code.entryBlock();
+
+ // Insert a new block between the last argument instruction and the first actual instruction
+ // of the method.
+ BasicBlock initClassBlock =
+ entryBlock.listIterator(code, arguments.size()).split(code, 0, null);
+ assert !initClassBlock.hasCatchHandlers();
+
+ InstructionListIterator iterator = initClassBlock.listIterator(code);
+ iterator.setInsertionPosition(entryBlock.exit().getPosition());
+ iterator.add(new InitClass(code.createValue(TypeLatticeElement.getInt()), target.holder()));
+ }
+
private void synthesizeNullCheckForReceiver(AppView<?> appView, IRCode code) {
List<Value> arguments = code.collectArguments();
if (!arguments.isEmpty()) {
@@ -1058,7 +1087,7 @@
assumeDynamicTypeRemover.finish();
classInitializationAnalysis.finish();
code.removeBlocks(blocksToRemove);
- code.removeAllTrivialPhis();
+ code.removeAllDeadAndTrivialPhis();
assert code.isConsistentSSA();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index e10b121..a1307a0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -126,6 +126,10 @@
return ConstraintWithTarget.ALWAYS;
}
+ public ConstraintWithTarget forInitClass(DexType clazz, DexType context) {
+ return ConstraintWithTarget.classIsVisible(context, clazz, appView);
+ }
+
public ConstraintWithTarget forInstanceGet(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 783ed8a..7e25c8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRMetadata;
-import com.android.tools.r8.ir.code.InstanceFieldInstruction;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -31,7 +31,6 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.StaticFieldInstruction;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
@@ -211,7 +210,7 @@
}
if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- replaceStaticFieldInstructionByClinitAccessIfPossible(
+ replaceInstructionByInitClassIfPossible(
staticGet, staticGet.getField().holder, code, iterator, code.method.holder());
}
replacement.setPosition(position);
@@ -226,7 +225,7 @@
private void rewriteInvokeMethodWithConstantValues(
IRCode code,
- DexType callingContext,
+ DexType context,
Set<Value> affectedValues,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
@@ -236,7 +235,7 @@
if (!invokedHolder.isClassType()) {
return;
}
- DexEncodedMethod target = current.lookupSingleTarget(appView, callingContext);
+ DexEncodedMethod target = current.lookupSingleTarget(appView, context);
if (target != null && target.isInstanceInitializer()) {
// Member value propagation does not apply to constructors. Removing a call to a constructor
// that is marked as having no side effects could lead to verification errors, due to
@@ -291,15 +290,27 @@
AbstractValue abstractReturnValue = target.getOptimizationInfo().getAbstractReturnValue();
if (abstractReturnValue.isSingleValue()) {
SingleValue singleReturnValue = abstractReturnValue.asSingleValue();
- if (singleReturnValue.isMaterializableInContext(appView, callingContext)) {
+ if (singleReturnValue.isMaterializableInContext(appView, context)) {
+ BasicBlock block = current.getBlock();
+ Position position = current.getPosition();
+
Instruction replacement =
singleReturnValue.createMaterializingInstruction(appView, code, current);
affectedValues.addAll(current.outValue().affectedValues());
+ current.moveDebugValues(replacement);
current.outValue().replaceUsers(replacement.outValue());
current.setOutValue(null);
- replacement.setPosition(current.getPosition());
- current.moveDebugValues(replacement);
- if (current.getBlock().hasCatchHandlers()) {
+
+ if (current.isInvokeMethodWithReceiver()) {
+ replaceInstructionByNullCheckIfPossible(current, iterator, context);
+ } else if (current.isInvokeStatic()) {
+ replaceInstructionByInitClassIfPossible(
+ current, target.holder(), code, iterator, context);
+ }
+
+ // Insert the definition of the replacement.
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
iterator.split(code, blocks).listIterator(code).add(replacement);
} else {
iterator.add(replacement);
@@ -354,47 +365,45 @@
Instruction replacement =
target.valueAsConstInstruction(code, current.outValue().getLocalInfo(), appView);
if (replacement != null) {
+ BasicBlock block = current.getBlock();
+ DexType context = code.method.holder();
+ Position position = current.getPosition();
+
+ // All usages are replaced by the replacement value.
affectedValues.addAll(current.outValue().affectedValues());
- DexType context = code.method.method.holder;
- if (current.instructionMayHaveSideEffects(appView, context)) {
- BasicBlock block = current.getBlock();
- Position position = current.getPosition();
+ current.outValue().replaceUsers(replacement.outValue());
- // All usages are replaced by the replacement value.
- current.outValue().replaceUsers(replacement.outValue());
-
- // To preserve side effects, original field-get is replaced by an explicit null-check, if
- // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
- if (current.isInstanceGet()) {
- replaceInstanceFieldInstructionByNullCheckIfPossible(
- current.asInstanceGet(), iterator, context);
- } else {
- replaceStaticFieldInstructionByClinitAccessIfPossible(
- current.asStaticGet(), target.holder(), code, iterator, context);
- }
-
- // Insert the definition of the replacement.
- replacement.setPosition(position);
- if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
- } else {
- iterator.add(replacement);
- }
+ // To preserve side effects, original field-get is replaced by an explicit null-check, if
+ // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
+ if (current.isInstanceGet()) {
+ replaceInstructionByNullCheckIfPossible(current, iterator, context);
} else {
- iterator.replaceCurrentInstruction(replacement);
+ replaceInstructionByInitClassIfPossible(current, target.holder(), code, iterator, context);
+ }
+
+ // Insert the definition of the replacement.
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
+ iterator.split(code, blocks).listIterator(code).add(replacement);
+ } else {
+ iterator.add(replacement);
}
feedback.markFieldAsPropagated(target);
}
}
- private void replaceInstanceFieldInstructionByNullCheckIfPossible(
- InstanceFieldInstruction instruction, InstructionListIterator iterator, DexType context) {
+ private void replaceInstructionByNullCheckIfPossible(
+ Instruction instruction, InstructionListIterator iterator, DexType context) {
+ assert instruction.isInstanceFieldInstruction() || instruction.isInvokeMethodWithReceiver();
assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
if (instruction.instructionMayHaveSideEffects(
- appView, context, FieldInstruction.Assumption.RECEIVER_NOT_NULL)) {
+ appView, context, Instruction.SideEffectAssumption.RECEIVER_NOT_NULL)) {
return;
}
- Value receiver = instruction.object();
+ Value receiver =
+ instruction.isInstanceFieldInstruction()
+ ? instruction.asInstanceFieldInstruction().object()
+ : instruction.asInvokeMethodWithReceiver().getReceiver();
if (receiver.isNeverNull()) {
iterator.removeOrReplaceByDebugLocalRead();
return;
@@ -410,6 +419,38 @@
iterator.replaceCurrentInstruction(replacement);
}
+ private void replaceInstructionByInitClassIfPossible(
+ Instruction instruction,
+ DexType holder,
+ IRCode code,
+ InstructionListIterator iterator,
+ DexType context) {
+ assert instruction.isStaticFieldInstruction() || instruction.isInvokeStatic();
+ if (instruction.instructionMayHaveSideEffects(
+ appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
+ return;
+ }
+ boolean classInitializationMayHaveSideEffects =
+ holder.classInitializationMayHaveSideEffects(
+ appView,
+ // Types that are a super type of `context` are guaranteed to be initialized
+ // already.
+ type -> appView.isSubtype(context, type).isTrue(),
+ Sets.newIdentityHashSet());
+ if (!classInitializationMayHaveSideEffects) {
+ iterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
+ if (!appView.canUseInitClass()) {
+ return;
+ }
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
+ if (clazz != null) {
+ Value dest = code.createValue(TypeLatticeElement.getInt());
+ iterator.replaceCurrentInstruction(new InitClass(dest, clazz.type));
+ }
+ }
+
private void replaceInstancePutByNullCheckIfNeverRead(
IRCode code, InstructionListIterator iterator, InstancePut current) {
DexEncodedField target = appView.appInfo().resolveField(current.getField());
@@ -421,10 +462,10 @@
return;
}
- replaceInstanceFieldInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
+ replaceInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
}
- private void replaceStaticPutByClinitAccessIfNeverRead(
+ private void replaceStaticPutByInitClassIfNeverRead(
IRCode code, InstructionListIterator iterator, StaticPut current) {
DexEncodedField field = appView.appInfo().resolveField(current.getField());
if (field == null || appView.appInfo().isFieldRead(field)) {
@@ -435,32 +476,10 @@
return;
}
- replaceStaticFieldInstructionByClinitAccessIfPossible(
+ replaceInstructionByInitClassIfPossible(
current, field.holder(), code, iterator, code.method.holder());
}
- private void replaceStaticFieldInstructionByClinitAccessIfPossible(
- StaticFieldInstruction instruction,
- DexType holder,
- IRCode code,
- InstructionListIterator iterator,
- DexType context) {
- if (instruction.instructionMayHaveSideEffects(
- appView, context, FieldInstruction.Assumption.CLASS_ALREADY_INITIALIZED)) {
- return;
- }
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(holder));
- if (clazz != null) {
- DexEncodedField clinitField =
- clazz.lookupStaticField(appView.dexItemFactory().objectMembers.clinitField);
- if (clinitField != null) {
- Value dest = code.createValue(TypeLatticeElement.getInt());
- StaticGet replacement = new StaticGet(dest, clinitField.field);
- iterator.replaceCurrentInstruction(replacement);
- }
- }
- }
-
/**
* Replace invoke targets and field accesses with constant values where possible.
*
@@ -488,7 +507,7 @@
} else if (current.isInstancePut()) {
replaceInstancePutByNullCheckIfNeverRead(code, iterator, current.asInstancePut());
} else if (current.isStaticPut()) {
- replaceStaticPutByClinitAccessIfNeverRead(code, iterator, current.asStaticPut());
+ replaceStaticPutByInitClassIfNeverRead(code, iterator, current.asStaticPut());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 698818f..de9d4cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
@@ -28,6 +29,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -54,6 +56,8 @@
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
// Maps keeping track of fields that have an already loaded value at basic block entry.
+ private final Map<BasicBlock, Set<DexType>> activeInitializedClassesAtEntry =
+ new IdentityHashMap<>();
private final Map<BasicBlock, Map<FieldAndObject, FieldValue>> activeInstanceFieldsAtEntry =
new IdentityHashMap<>();
private final Map<BasicBlock, Map<DexField, FieldValue>> activeStaticFieldsAtEntry =
@@ -61,6 +65,7 @@
// Maps keeping track of fields with already loaded values for the current block during
// elimination.
+ private Set<DexType> activeInitializedClasses;
private Map<FieldAndObject, FieldValue> activeInstanceFieldValues;
private Map<DexField, FieldValue> activeStaticFieldValues;
@@ -156,6 +161,10 @@
public void run() {
DexType context = method.method.holder;
for (BasicBlock block : dominatorTree.getSortedBlocks()) {
+ activeInitializedClasses =
+ activeInitializedClassesAtEntry.containsKey(block)
+ ? activeInitializedClassesAtEntry.get(block)
+ : Sets.newIdentityHashSet();
activeInstanceFieldValues =
activeInstanceFieldsAtEntry.containsKey(block)
? activeInstanceFieldsAtEntry.get(block)
@@ -217,6 +226,12 @@
killActiveFields(staticPut);
activeStaticFieldValues.put(field, new ExistingValue(staticPut.value()));
}
+ } else if (instruction.isInitClass()) {
+ InitClass initClass = instruction.asInitClass();
+ assert !initClass.outValue().hasAnyUsers();
+ if (activeInitializedClasses.contains(initClass.getClassValue())) {
+ it.removeOrReplaceByDebugLocalRead();
+ }
} else if (instruction.isMonitor()) {
if (instruction.asMonitor().isEnter()) {
killAllActiveFields();
@@ -271,7 +286,7 @@
: "Unexpected instruction of type " + instruction.getClass().getTypeName();
}
}
- propagateActiveFieldsFrom(block);
+ propagateActiveStateFrom(block);
}
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
@@ -324,17 +339,24 @@
});
}
- private void propagateActiveFieldsFrom(BasicBlock block) {
+ private void propagateActiveStateFrom(BasicBlock block) {
for (BasicBlock successor : block.getSuccessors()) {
// Allow propagation across exceptional edges, just be careful not to propagate if the
// throwing instruction is a field instruction.
if (successor.getPredecessors().size() == 1) {
if (block.hasCatchSuccessor(successor)) {
Instruction exceptionalExit = block.exceptionalExit();
- if (exceptionalExit != null && exceptionalExit.isFieldInstruction()) {
- killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction());
+ if (exceptionalExit != null) {
+ if (exceptionalExit.isFieldInstruction()) {
+ killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction());
+ } else if (exceptionalExit.isInitClass()) {
+ killActiveInitializedClassesForExceptionalExit(exceptionalExit.asInitClass());
+ }
}
}
+ assert !activeInitializedClassesAtEntry.containsKey(successor);
+ activeInitializedClassesAtEntry.put(
+ successor, SetUtils.newIdentityHashSet(activeInitializedClasses));
assert !activeInstanceFieldsAtEntry.containsKey(successor);
activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFieldValues));
assert !activeStaticFieldsAtEntry.containsKey(successor);
@@ -393,4 +415,8 @@
activeStaticFieldValues.remove(field);
}
}
+
+ private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) {
+ activeInitializedClasses.remove(instruction.getClassValue());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index b8ca4bc..8e0858a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -47,7 +47,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
@@ -193,33 +192,34 @@
}
// Change the return type of direct methods that return an uninstantiated type to void.
- List<DexEncodedMethod> directMethods = clazz.directMethods();
- for (int i = 0; i < directMethods.size(); ++i) {
- DexEncodedMethod encodedMethod = directMethods.get(i);
- DexMethod method = encodedMethod.method;
- RewrittenPrototypeDescription prototypeChanges =
- prototypeChangesPerMethod.getOrDefault(
- encodedMethod, RewrittenPrototypeDescription.none());
- ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
- DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
- if (newMethod != method) {
- Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+ clazz
+ .getMethodCollection()
+ .replaceDirectMethods(
+ encodedMethod -> {
+ DexMethod method = encodedMethod.method;
+ RewrittenPrototypeDescription prototypeChanges =
+ prototypeChangesPerMethod.getOrDefault(
+ encodedMethod, RewrittenPrototypeDescription.none());
+ ArgumentInfoCollection removedArgumentsInfo =
+ prototypeChanges.getArgumentInfoCollection();
+ DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+ if (newMethod != method) {
+ Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
- // TODO(b/110806787): Can be extended to handle collisions by renaming the given
- // method.
- if (usedSignatures.add(wrapper)) {
- clazz.setDirectMethod(
- i,
- encodedMethod.toTypeSubstitutedMethod(
- newMethod,
- removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod)));
- methodMapping.put(method, newMethod);
- if (removedArgumentsInfo.hasRemovedArguments()) {
- removedArgumentsInfoPerMethod.put(newMethod, removedArgumentsInfo);
- }
- }
- }
- }
+ // TODO(b/110806787): Can be extended to handle collisions by renaming the given
+ // method.
+ if (usedSignatures.add(wrapper)) {
+ methodMapping.put(method, newMethod);
+ if (removedArgumentsInfo.hasRemovedArguments()) {
+ removedArgumentsInfoPerMethod.put(newMethod, removedArgumentsInfo);
+ }
+ return encodedMethod.toTypeSubstitutedMethod(
+ newMethod,
+ removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod));
+ }
+ }
+ return encodedMethod;
+ });
// Change the return type of virtual methods that return an uninstantiated type to void.
// This is done in two steps. First we change the return type of all methods that override
@@ -227,66 +227,74 @@
// all supertypes of the current class are always visited prior to the current class.
// This is important to ensure that a method that used to override a method in its super
// class will continue to do so after this optimization.
- List<DexEncodedMethod> virtualMethods = clazz.virtualMethods();
- for (int i = 0; i < virtualMethods.size(); ++i) {
- DexEncodedMethod encodedMethod = virtualMethods.get(i);
- DexMethod method = encodedMethod.method;
- RewrittenPrototypeDescription prototypeChanges =
- getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
- ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
- DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
- if (newMethod != method) {
- Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+ clazz
+ .getMethodCollection()
+ .replaceVirtualMethods(
+ encodedMethod -> {
+ DexMethod method = encodedMethod.method;
+ RewrittenPrototypeDescription prototypeChanges =
+ getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
+ ArgumentInfoCollection removedArgumentsInfo =
+ prototypeChanges.getArgumentInfoCollection();
+ DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+ if (newMethod != method) {
+ Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
- boolean isOverrideOfPreviouslyChangedMethodInSuperClass =
- changedVirtualMethods.getOrDefault(equivalence.wrap(method), ImmutableSet.of()).stream()
- .anyMatch(other -> appView.appInfo().isSubtype(clazz.type, other));
- if (isOverrideOfPreviouslyChangedMethodInSuperClass) {
- assert methodPool.hasSeen(wrapper);
+ boolean isOverrideOfPreviouslyChangedMethodInSuperClass =
+ changedVirtualMethods
+ .getOrDefault(equivalence.wrap(method), ImmutableSet.of())
+ .stream()
+ .anyMatch(other -> appView.appInfo().isSubtype(clazz.type, other));
+ if (isOverrideOfPreviouslyChangedMethodInSuperClass) {
+ assert methodPool.hasSeen(wrapper);
- boolean signatureIsAvailable = usedSignatures.add(wrapper);
- assert signatureIsAvailable;
+ boolean signatureIsAvailable = usedSignatures.add(wrapper);
+ assert signatureIsAvailable;
- clazz.setVirtualMethod(
- i,
- encodedMethod.toTypeSubstitutedMethod(
- newMethod,
- removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod)));
- methodMapping.put(method, newMethod);
- }
- }
- }
- for (int i = 0; i < virtualMethods.size(); ++i) {
- DexEncodedMethod encodedMethod = virtualMethods.get(i);
- DexMethod method = encodedMethod.method;
- RewrittenPrototypeDescription prototypeChanges =
- getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
- ArgumentInfoCollection removedArgumentsInfo = prototypeChanges.getArgumentInfoCollection();
- DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
- if (newMethod != method) {
- Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+ methodMapping.put(method, newMethod);
+ return encodedMethod.toTypeSubstitutedMethod(
+ newMethod,
+ removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod));
+ }
+ }
+ return encodedMethod;
+ });
+ clazz
+ .getMethodCollection()
+ .replaceVirtualMethods(
+ encodedMethod -> {
+ DexMethod method = encodedMethod.method;
+ RewrittenPrototypeDescription prototypeChanges =
+ getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
+ ArgumentInfoCollection removedArgumentsInfo =
+ prototypeChanges.getArgumentInfoCollection();
+ DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+ if (newMethod != method) {
+ Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
- // TODO(b/110806787): Can be extended to handle collisions by renaming the given
- // method. Note that this also requires renaming all of the methods that override this
- // method, though.
- if (!methodPool.hasSeen(wrapper) && usedSignatures.add(wrapper)) {
- methodPool.seen(wrapper);
+ // TODO(b/110806787): Can be extended to handle collisions by renaming the given
+ // method. Note that this also requires renaming all of the methods that override
+ // this
+ // method, though.
+ if (!methodPool.hasSeen(wrapper) && usedSignatures.add(wrapper)) {
+ methodPool.seen(wrapper);
- clazz.setVirtualMethod(
- i,
- encodedMethod.toTypeSubstitutedMethod(
- newMethod,
- removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod)));
- methodMapping.put(method, newMethod);
+ methodMapping.put(method, newMethod);
- boolean added =
- changedVirtualMethods
- .computeIfAbsent(equivalence.wrap(method), key -> Sets.newIdentityHashSet())
- .add(clazz.type);
- assert added;
- }
- }
- }
+ boolean added =
+ changedVirtualMethods
+ .computeIfAbsent(
+ equivalence.wrap(method), key -> Sets.newIdentityHashSet())
+ .add(clazz.type);
+ assert added;
+
+ return encodedMethod.toTypeSubstitutedMethod(
+ newMethod,
+ removedArgumentsInfo.createParameterAnnotationsRemover(encodedMethod));
+ }
+ }
+ return encodedMethod;
+ });
}
private RewrittenPrototypeDescription getPrototypeChanges(
@@ -385,7 +393,7 @@
}
assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
code.removeBlocks(blocksToBeRemoved);
- code.removeAllTrivialPhis(valuesToNarrow);
+ code.removeAllDeadAndTrivialPhis(valuesToNarrow);
code.removeUnreachableBlocks();
if (!valuesToNarrow.isEmpty()) {
new TypeAnalysis(appView).narrowing(valuesToNarrow);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 7c71bd9..db393ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -35,7 +35,6 @@
import java.util.BitSet;
import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -224,58 +223,64 @@
signatures.markSignatureAsUsed(method.method);
}
- List<DexEncodedMethod> directMethods = clazz.directMethods();
- for (int i = 0; i < directMethods.size(); i++) {
- DexEncodedMethod method = directMethods.get(i);
+ clazz
+ .getMethodCollection()
+ .replaceDirectMethods(
+ method -> {
- // If this is a method with known resolution issues, then don't remove any unused arguments.
- if (appView.appInfo().failedResolutionTargets.contains(method.method)) {
- continue;
- }
+ // If this is a method with known resolution issues, then don't remove any unused
+ // arguments.
+ if (appView.appInfo().failedResolutionTargets.contains(method.method)) {
+ return method;
+ }
- ArgumentInfoCollection unused = collectUnusedArguments(method);
- if (unused != null && unused.hasRemovedArguments()) {
- DexProto newProto = createProtoWithRemovedArguments(method, unused);
- DexMethod newSignature = signatures.getNewSignature(method, newProto);
- if (newSignature == null) {
- assert appView.dexItemFactory().isConstructor(method.method);
- continue;
- }
- DexEncodedMethod newMethod = signatures.removeArguments(method, newSignature, unused);
- clazz.setDirectMethod(i, newMethod);
- synchronized (this) {
- methodMapping.put(method.method, newMethod.method);
- removedArguments.put(newMethod.method, unused);
- }
- }
- }
+ ArgumentInfoCollection unused = collectUnusedArguments(method);
+ if (unused != null && unused.hasRemovedArguments()) {
+ DexProto newProto = createProtoWithRemovedArguments(method, unused);
+ DexMethod newSignature = signatures.getNewSignature(method, newProto);
+ if (newSignature == null) {
+ assert appView.dexItemFactory().isConstructor(method.method);
+ return method;
+ }
+ DexEncodedMethod newMethod =
+ signatures.removeArguments(method, newSignature, unused);
+ synchronized (this) {
+ methodMapping.put(method.method, newMethod.method);
+ removedArguments.put(newMethod.method, unused);
+ }
+ return newMethod;
+ }
+ return method;
+ });
}
private void processVirtualMethods(DexProgramClass clazz) {
MemberPool<DexMethod> methodPool = methodPoolCollection.get(clazz);
GloballyUsedSignatures signatures = new GloballyUsedSignatures(methodPool);
- List<DexEncodedMethod> virtualMethods = clazz.virtualMethods();
- for (int i = 0; i < virtualMethods.size(); i++) {
- DexEncodedMethod method = virtualMethods.get(i);
- ArgumentInfoCollection unused = collectUnusedArguments(method, methodPool);
- if (unused != null && unused.hasRemovedArguments()) {
- DexProto newProto = createProtoWithRemovedArguments(method, unused);
- DexMethod newSignature = signatures.getNewSignature(method, newProto);
+ clazz
+ .getMethodCollection()
+ .replaceVirtualMethods(
+ method -> {
+ ArgumentInfoCollection unused = collectUnusedArguments(method, methodPool);
+ if (unused != null && unused.hasRemovedArguments()) {
+ DexProto newProto = createProtoWithRemovedArguments(method, unused);
+ DexMethod newSignature = signatures.getNewSignature(method, newProto);
- // Double-check that the new method signature is in fact available.
- assert !methodPool.hasSeenStrictlyAbove(equivalence.wrap(newSignature));
- assert !methodPool.hasSeenStrictlyBelow(equivalence.wrap(newSignature));
+ // Double-check that the new method signature is in fact available.
+ assert !methodPool.hasSeenStrictlyAbove(equivalence.wrap(newSignature));
+ assert !methodPool.hasSeenStrictlyBelow(equivalence.wrap(newSignature));
- DexEncodedMethod newMethod =
- signatures.removeArguments(
- method, signatures.getNewSignature(method, newProto), unused);
- clazz.setVirtualMethod(i, newMethod);
+ DexEncodedMethod newMethod =
+ signatures.removeArguments(
+ method, signatures.getNewSignature(method, newProto), unused);
- methodMapping.put(method.method, newMethod.method);
- removedArguments.put(newMethod.method, unused);
- }
- }
+ methodMapping.put(method.method, newMethod.method);
+ removedArguments.put(newMethod.method, unused);
+ return newMethod;
+ }
+ return method;
+ });
}
private ArgumentInfoCollection collectUnusedArguments(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 6707fe1..9b443bc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -270,7 +270,7 @@
// Restore normality.
Set<Value> affectedValues = Sets.newIdentityHashSet();
- code.removeAllTrivialPhis(affectedValues);
+ code.removeAllDeadAndTrivialPhis(affectedValues);
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index e96b211..5e8b552 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClass.FieldSetter;
-import com.android.tools.r8.graph.DexClass.MethodSetter;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -17,6 +16,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
@@ -559,8 +560,7 @@
assert clazz.instanceFields().size() == 0;
clearEnumtoUnboxMethods(clazz);
} else {
- fixupMethods(clazz.directMethods(), clazz::setDirectMethod);
- fixupMethods(clazz.virtualMethods(), clazz::setVirtualMethod);
+ clazz.getMethodCollection().replaceMethods(this::fixupMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
@@ -586,19 +586,13 @@
}
}
- private void fixupMethods(List<DexEncodedMethod> methods, MethodSetter setter) {
- if (methods == null) {
- return;
+ private DexEncodedMethod fixupMethod(DexEncodedMethod encodedMethod) {
+ DexMethod newMethod = fixupMethod(encodedMethod.method);
+ if (newMethod != encodedMethod.method) {
+ lensBuilder.move(encodedMethod.method, newMethod, encodedMethod.isStatic());
+ return encodedMethod.toTypeSubstitutedMethod(newMethod);
}
- for (int i = 0; i < methods.size(); i++) {
- DexEncodedMethod encodedMethod = methods.get(i);
- DexMethod method = encodedMethod.method;
- DexMethod newMethod = fixupMethod(method);
- if (newMethod != method) {
- lensBuilder.move(method, newMethod, encodedMethod.isStatic());
- setter.setMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
- }
- }
+ return encodedMethod;
}
private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
@@ -612,7 +606,13 @@
if (newType != field.type) {
DexField newField = factory.createField(field.holder, newType, field.name);
lensBuilder.move(field, newField);
- setter.setField(i, encodedField.toTypeSubstitutedField(newField));
+ DexEncodedField newEncodedField = encodedField.toTypeSubstitutedField(newField);
+ setter.setField(i, newEncodedField);
+ if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
+ assert encodedField.getStaticValue() == DexValueNull.NULL;
+ newEncodedField.setStaticValue(DexValueInt.DEFAULT);
+ // TODO(b/150593449): Support conversion from DexValueEnum to DexValueInt.
+ }
}
}
}
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 84e4fdc..bbf2f22 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
@@ -19,6 +19,7 @@
import static com.android.tools.r8.ir.code.Opcodes.DIV;
import static com.android.tools.r8.ir.code.Opcodes.GOTO;
import static com.android.tools.r8.ir.code.Opcodes.IF;
+import static com.android.tools.r8.ir.code.Opcodes.INIT_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_OF;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
@@ -94,6 +95,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
@@ -286,9 +288,6 @@
List<Value> values = code.collectArguments();
for (int i = 0; i < values.size(); i++) {
Value value = values.get(i);
- if (value.numberOfPhiUsers() > 0) {
- continue;
- }
ParameterUsage usage = collectParameterUsages(i, value);
if (usage != null) {
usages.add(usage);
@@ -301,12 +300,23 @@
: new ParameterUsagesInfo(usages));
}
- private ParameterUsage collectParameterUsages(int i, Value value) {
- ParameterUsageBuilder builder = new ParameterUsageBuilder(value, i, dexItemFactory);
- for (Instruction user : value.aliasedUsers()) {
- if (!builder.note(user)) {
+ private ParameterUsage collectParameterUsages(int i, Value root) {
+ ParameterUsageBuilder builder = new ParameterUsageBuilder(root, i, dexItemFactory);
+ WorkList<Value> worklist = WorkList.newIdentityWorkList();
+ worklist.addIfNotSeen(root);
+ while (worklist.hasNext()) {
+ Value value = worklist.next();
+ if (value.hasPhiUsers()) {
return null;
}
+ for (Instruction user : value.uniqueUsers()) {
+ if (!builder.note(user)) {
+ return null;
+ }
+ if (user.isAssume()) {
+ worklist.addIfNotSeen(user.outValue());
+ }
+ }
}
return builder.build();
}
@@ -442,6 +452,7 @@
case CONST_STRING:
case DEX_ITEM_BASED_CONST_STRING:
case DIV:
+ case INIT_CLASS:
case INSTANCE_OF:
case MUL:
case NEW_ARRAY_EMPTY:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
index f10c3fe..1648958 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.code.ConstMethodType;
import com.android.tools.r8.ir.code.DefaultInstructionVisitor;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -62,6 +63,8 @@
boolean isValidNewInstance(CodeProcessor context, NewInstance invoke);
+ boolean isValidInitClass(CodeProcessor context, DexType clazz);
+
void patch(ApplyStrategy context, NewInstance newInstance);
void patch(ApplyStrategy context, InvokeMethod invoke);
@@ -69,6 +72,8 @@
void patch(ApplyStrategy context, InstanceGet instanceGet);
void patch(ApplyStrategy context, StaticGet staticGet);
+
+ void patch(ApplyStrategy context, InitClass initClass);
}
// No-op strategy.
@@ -110,6 +115,11 @@
}
@Override
+ public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
+ return false;
+ }
+
+ @Override
public void patch(ApplyStrategy context, NewInstance newInstance) {
throw new Unreachable();
}
@@ -128,6 +138,11 @@
public void patch(ApplyStrategy context, StaticGet staticGet) {
throw new Unreachable();
}
+
+ @Override
+ public void patch(ApplyStrategy context, InitClass initClass) {
+ throw new Unreachable();
+ }
};
public final AppView<AppInfoWithLiveness> appView;
@@ -353,6 +368,21 @@
return null;
}
+ @Override
+ public Void visit(InitClass initClass) {
+ DexType clazz = initClass.getClassValue();
+ Strategy strategy = strategyProvider.apply(clazz);
+ if (strategy.isValidInitClass(this, clazz)) {
+ if (shouldRewrite(clazz)) {
+ // Only rewrite references to lambda classes if we are outside the class.
+ process(strategy, initClass);
+ }
+ } else {
+ lambdaChecker.accept(clazz);
+ }
+ return null;
+ }
+
abstract void process(Strategy strategy, InvokeMethod invokeMethod);
abstract void process(Strategy strategy, NewInstance newInstance);
@@ -364,4 +394,6 @@
abstract void process(Strategy strategy, StaticPut staticPut);
abstract void process(Strategy strategy, StaticGet staticGet);
+
+ abstract void process(Strategy strategy, InitClass initClass);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 176db10..510c122 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
@@ -529,6 +530,11 @@
void process(Strategy strategy, StaticGet staticGet) {
queueForProcessing(method);
}
+
+ @Override
+ void process(Strategy strategy, InitClass initClass) {
+ queueForProcessing(method);
+ }
}
public final class ApplyStrategy extends CodeProcessor {
@@ -644,6 +650,11 @@
void process(Strategy strategy, StaticGet staticGet) {
strategy.patch(this, staticGet);
}
+
+ @Override
+ void process(Strategy strategy, InitClass initClass) {
+ strategy.patch(this, initClass);
+ }
}
private final class LambdaMergerOptimizationInfoFixer
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index e54d241..81480d6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -50,10 +51,10 @@
assert group.containsLambda(lambda);
// Only support writes to singleton static field named 'INSTANCE' from lambda
// static class initializer.
- return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName &&
- lambda == field.type &&
- context.factory.isClassConstructor(context.method.method) &&
- context.method.method.holder == lambda;
+ return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
+ && lambda == field.type
+ && context.factory.isClassConstructor(context.method.method)
+ && context.method.method.holder == lambda;
}
@Override
@@ -61,8 +62,8 @@
DexType lambda = field.holder;
assert group.containsLambda(lambda);
// Support all reads of singleton static field named 'INSTANCE'.
- return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName &&
- lambda == field.type;
+ return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
+ && lambda == field.type;
}
@Override
@@ -112,6 +113,13 @@
}
@Override
+ public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
+ assert group.containsLambda(clazz);
+ // Support all init class instructions.
+ return true;
+ }
+
+ @Override
public void patch(ApplyStrategy context, NewInstance newInstance) {
DexType oldType = newInstance.clazz;
DexType newType = group.getGroupClassType();
@@ -202,6 +210,14 @@
context.recordTypeHasChanged(patchedStaticGet.outValue());
}
+ @Override
+ public void patch(ApplyStrategy context, InitClass initClass) {
+ InitClass pachedInitClass =
+ new InitClass(
+ context.code.createValue(TypeLatticeElement.getInt()), group.getGroupClassType());
+ context.instructions().replaceCurrentInstruction(pachedInitClass);
+ }
+
private void patchInitializer(CodeProcessor context, InvokeDirect invoke) {
// Patching includes:
// - change of methods
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 8978657..3c99a56 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -659,6 +659,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return registerTypeReference(clazz);
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
return registerMethod(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 8e8d2da..6c714c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -828,6 +828,9 @@
public boolean isSupportedAppendMethod(InvokeMethod invoke) {
DexMethod invokedMethod = invoke.getInvokedMethod();
assert isAppendMethod(invokedMethod);
+ if (invoke.hasOutValue()) {
+ return false;
+ }
// Any methods other than append(arg) are not trivial since they may change the builder
// state not monotonically.
if (invoke.inValues().size() > 2) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 4a65b7c..a37a42b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -13,7 +13,10 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
@@ -30,6 +33,9 @@
KmClass kmClass;
+ DexField companionObject = null;
+ DexProgramClass hostClass = null;
+
static KotlinClass fromKotlinClassMetadata(
KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
assert kotlinClassMetadata instanceof KotlinClassMetadata.Class;
@@ -41,6 +47,28 @@
super(metadata, clazz);
}
+ void foundCompanionObject(DexEncodedField companionObject) {
+ // Companion cannot be nested. If this class is a host (and about to store a field that holds
+ // a companion object), it should not have a host class.
+ assert hostClass == null;
+ this.companionObject = companionObject.field;
+ }
+
+ boolean hasCompanionObject() {
+ return companionObject != null;
+ }
+
+ DexType getCompanionObjectType() {
+ return hasCompanionObject() ? companionObject.type : null;
+ }
+
+ void linkHostClass(DexProgramClass hostClass) {
+ // Companion cannot be nested. If this class is a companion object (and about to link to its
+ // host class), it should not have a companion object.
+ assert companionObject == null;
+ this.hostClass = hostClass;
+ }
+
@Override
void processMetadata(KotlinClassMetadata.Class metadata) {
kmClass = metadata.toKmClass();
@@ -78,10 +106,19 @@
superTypes.add(toKmType(addKotlinPrefix("Any;")));
}
- // Rewriting downward hierarchies: nested.
+ // Rewriting downward hierarchies: nested, including companion class.
+ // Note that `kotlinc` uses these nested classes to determine which classes to look up when
+ // resolving declarations in the companion object, e.g., Host.Companion.prop and Host.prop.
+ // Thus, users (in particular, library developers) should keep InnerClasses and EnclosingMethod
+ // attributes if declarations in the companion need to be exposed.
List<String> nestedClasses = kmClass.getNestedClasses();
nestedClasses.clear();
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ // Skip InnerClass attribute for itself.
+ // Otherwise, an inner class would have itself as a nested class.
+ if (clazz.getInnerClassAttributeForThisClass() == innerClassAttribute) {
+ continue;
+ }
DexString renamedInnerName = lens.lookupInnerName(innerClassAttribute, appView.options());
if (renamedInnerName != null) {
nestedClasses.add(renamedInnerName.toString());
@@ -103,6 +140,8 @@
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
+
+ // Rewriting constructors.
List<KmConstructor> constructors = kmClass.getConstructors();
constructors.clear();
for (DexEncodedMethod method : clazz.directMethods()) {
@@ -115,9 +154,14 @@
}
}
- // TODO(b/70169921): enum entries
+ // Rewriting companion object if any.
+ if (kmClass.getCompanionObject() != null && hasCompanionObject()) {
+ kmClass.setCompanionObject(lens.lookupName(companionObject).toString());
+ }
- rewriteDeclarationContainer(kmClass, appView, lens);
+ // TODO(b/151193864): enum entries
+
+ rewriteDeclarationContainer(appView, lens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index 4433017..cc1cd14 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -21,7 +21,7 @@
public final class KotlinClassFacade extends KotlinInfo<KotlinClassMetadata.MultiFileClassFacade> {
- // TODO(b/70169921): is it better to maintain List<DexType>?
+ // TODO(b/151194869): is it better to maintain List<DexType>?
List<String> partClassNames;
static KotlinClassFacade fromKotlinClassMetadata(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index f108252..16223bb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -19,7 +19,7 @@
public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> {
KmPackage kmPackage;
- // TODO(b/70169921): is it better to maintain DexType?
+ // TODO(b/151194869): is it better to maintain DexType?
String facadeClassName;
static KotlinClassPart fromKotlinClassMetadata(
@@ -48,7 +48,7 @@
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(kmPackage, appView, lens);
+ rewriteDeclarationContainer(appView, lens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index bb4dc47..27c9230 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -38,7 +38,7 @@
if (!appView.options().enableKotlinMetadataRewritingForMembers) {
return;
}
- rewriteDeclarationContainer(kmPackage, appView, lens);
+ rewriteDeclarationContainer(appView, lens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index e9c1eb6..7c53cba 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
+import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
@@ -17,6 +18,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
@@ -93,15 +95,49 @@
return isClass() || isFile() || isClassPart();
}
+ KmDeclarationContainer getDeclarations() {
+ if (isClass()) {
+ return asClass().kmClass;
+ } else if (isFile()) {
+ return asFile().kmPackage;
+ } else if (isClassPart()) {
+ return asClassPart().kmPackage;
+ } else {
+ throw new Unreachable("Unexpected KotlinInfo: " + this);
+ }
+ }
+
// {@link KmClass} and {@link KmPackage} are inherited from {@link KmDeclarationContainer} that
// abstract functions and properties. Rewriting of those portions can be unified here.
- void rewriteDeclarationContainer(
- KmDeclarationContainer kmDeclarationContainer,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens) {
+ void rewriteDeclarationContainer(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
assert clazz != null;
+ KmDeclarationContainer kmDeclarationContainer = getDeclarations();
Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>();
+
+ // Backing fields for a companion object are declared in its host class.
+ Iterable<DexEncodedField> fields = clazz.fields();
+ Predicate<DexEncodedField> backingFieldTester = DexEncodedField::isKotlinBackingField;
+ if (isClass()) {
+ KotlinClass ktClass = asClass();
+ if (IS_COMPANION_OBJECT.invoke(ktClass.kmClass.getFlags()) && ktClass.hostClass != null) {
+ fields = ktClass.hostClass.fields();
+ backingFieldTester = DexEncodedField::isKotlinBackingFieldForCompanionObject;
+ }
+ }
+
+ for (DexEncodedField field : fields) {
+ if (backingFieldTester.test(field)) {
+ String name = field.getKotlinMemberInfo().propertyName;
+ assert name != null;
+ KmPropertyGroup.Builder builder =
+ propertyGroupBuilderMap.computeIfAbsent(
+ name,
+ k -> KmPropertyGroup.builder(field.getKotlinMemberInfo().propertyFlags, name));
+ builder.foundBackingField(field);
+ }
+ }
+
List<KmFunction> functions = kmDeclarationContainer.getFunctions();
functions.clear();
for (DexEncodedMethod method : clazz.methods()) {
@@ -121,19 +157,22 @@
assert name != null;
KmPropertyGroup.Builder builder =
propertyGroupBuilderMap.computeIfAbsent(
- name, k -> KmPropertyGroup.builder(method.getKotlinMemberInfo().flag, name));
+ name,
+ // Hitting here (creating a property builder) after visiting all fields means that
+ // this property doesn't have a backing field. Don't use members' flags.
+ k -> KmPropertyGroup.builder(method.getKotlinMemberInfo().propertyFlags, name));
switch (method.getKotlinMemberInfo().memberKind) {
case EXTENSION_PROPERTY_GETTER:
builder.isExtensionGetter();
// fallthrough;
case PROPERTY_GETTER:
- builder.foundGetter(method);
+ builder.foundGetter(method, method.getKotlinMemberInfo().flags);
break;
case EXTENSION_PROPERTY_SETTER:
builder.isExtensionSetter();
// fallthrough;
case PROPERTY_SETTER:
- builder.foundSetter(method);
+ builder.foundSetter(method, method.getKotlinMemberInfo().flags);
break;
case EXTENSION_PROPERTY_ANNOTATIONS:
builder.isExtensionAnnotations();
@@ -147,18 +186,7 @@
continue;
}
- // TODO(b/70169921): What should we do for methods that fall into this category---no mark?
- }
-
- for (DexEncodedField field : clazz.fields()) {
- if (field.isKotlinBackingField()) {
- String name = field.getKotlinMemberInfo().propertyName;
- assert name != null;
- KmPropertyGroup.Builder builder =
- propertyGroupBuilderMap.computeIfAbsent(
- name, k -> KmPropertyGroup.builder(field.getKotlinMemberInfo().flag, name));
- builder.foundBackingField(field);
- }
+ // TODO(b/151194869): What should we do for methods that fall into this category---no mark?
}
List<KmProperty> properties = kmDeclarationContainer.getProperties();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
new file mode 100644
index 0000000..9d89660
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class KotlinInfoCollector {
+ public static void computeKotlinInfoForProgramClasses(
+ DexApplication application, AppView<?> appView, ExecutorService executorService)
+ throws ExecutionException {
+ if (appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations) {
+ return;
+ }
+ Kotlin kotlin = appView.dexItemFactory().kotlin;
+ Reporter reporter = appView.options().reporter;
+ Map<DexProgramClass, DexProgramClass> companionToHostMap = new ConcurrentHashMap<>();
+ ThreadUtils.processItems(
+ application.classes(),
+ programClass -> {
+ KotlinInfo kotlinInfo = kotlin.getKotlinInfo(programClass, reporter);
+ programClass.setKotlinInfo(kotlinInfo);
+ KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo, reporter);
+ // Store a companion type to revisit.
+ if (kotlinInfo != null
+ && kotlinInfo.isClass()
+ && kotlinInfo.asClass().hasCompanionObject()) {
+ DexType companionType = kotlinInfo.asClass().getCompanionObjectType();
+ DexProgramClass companionClass = appView.definitionForProgramType(companionType);
+ if (companionClass != null) {
+ companionToHostMap.put(companionClass, programClass);
+ }
+ }
+ },
+ executorService);
+ // TODO(b/151194869): if we can guarantee that Companion classes are visited ahead and their
+ // KotlinInfo is created before processing host classes, below could be hoisted to 1st pass.
+ // Maybe name-based filtering? E.g., classes whose name ends with "$Companion" v.s. not?
+ ThreadUtils.processItems(
+ companionToHostMap.keySet(),
+ companionClass -> {
+ KotlinInfo kotlinInfo = companionClass.getKotlinInfo();
+ if (kotlinInfo != null && kotlinInfo.isClass()) {
+ DexProgramClass hostClass = companionToHostMap.get(companionClass);
+ assert hostClass != null;
+ kotlinInfo.asClass().linkHostClass(hostClass);
+ // Revisit host class's members with declarations in the companion object.
+ KotlinMemberInfo.markKotlinMemberInfo(hostClass, kotlinInfo, reporter);
+ }
+ },
+ executorService);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
index 50058ec..dfdb28b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -37,8 +36,12 @@
}
public final MemberKind memberKind;
- // Original member flag. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
- final int flag;
+ // Original member flags. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
+ final int flags;
+ // TODO(b/151194869): better to split into FunctionInfo v.s. PropertyInfo ?
+ // Original property flags. E.g., for property getter, getter flags are stored to `flags`, while
+ // the property's flags are stored here, in case of properties without a backing field.
+ final int propertyFlags;
// Original property name for (extension) property. Otherwise, null.
final String propertyName;
// Information from original KmValueParameter(s) if available. Otherwise, null.
@@ -46,32 +49,40 @@
// Constructor for KmFunction
private KotlinMemberInfo(
- MemberKind memberKind, int flag, List<KmValueParameter> kmValueParameters) {
- this(memberKind, flag, null, kmValueParameters);
+ MemberKind memberKind, int flags, List<KmValueParameter> kmValueParameters) {
+ this(memberKind, flags, 0, null, kmValueParameters);
}
// Constructor for a backing field and a getter in KmProperty
- private KotlinMemberInfo(MemberKind memberKind, int flag, String propertyName) {
- this(memberKind, flag, propertyName, EMPTY_PARAM);
+ private KotlinMemberInfo(
+ MemberKind memberKind, int flags, int propertyFlags, String propertyName) {
+ this(memberKind, flags, propertyFlags, propertyName, EMPTY_PARAM);
}
// Constructor for a setter in KmProperty
private KotlinMemberInfo(
MemberKind memberKind,
- int flag,
+ int flags,
+ int propertyFlags,
String propertyName,
KmValueParameter kmValueParameter) {
- this(memberKind, flag, propertyName,
+ this(
+ memberKind,
+ flags,
+ propertyFlags,
+ propertyName,
kmValueParameter != null ? ImmutableList.of(kmValueParameter) : EMPTY_PARAM);
}
private KotlinMemberInfo(
MemberKind memberKind,
- int flag,
+ int flags,
+ int propertyFlags,
String propertyName,
List<KmValueParameter> kmValueParameters) {
this.memberKind = memberKind;
- this.flag = flag;
+ this.flags = flags;
+ this.propertyFlags = propertyFlags;
this.propertyName = propertyName;
assert kmValueParameters != null;
if (kmValueParameters.isEmpty()) {
@@ -100,6 +111,7 @@
FUNCTION,
EXTENSION_FUNCTION,
+ COMPANION_OBJECT_BACKING_FIELD,
PROPERTY_BACKING_FIELD,
PROPERTY_GETTER,
PROPERTY_SETTER,
@@ -110,8 +122,6 @@
EXTENSION_PROPERTY_SETTER,
EXTENSION_PROPERTY_ANNOTATIONS;
- // TODO(b/70169921): companion
-
public boolean isFunction() {
return this == FUNCTION || isExtensionFunction();
}
@@ -124,8 +134,13 @@
return this == PROPERTY_BACKING_FIELD;
}
+ public boolean isBackingFieldForCompanionObject() {
+ return this == COMPANION_OBJECT_BACKING_FIELD;
+ }
+
public boolean isProperty() {
return isBackingField()
+ || isBackingFieldForCompanionObject()
|| this == PROPERTY_GETTER
|| this == PROPERTY_SETTER
|| this == PROPERTY_ANNOTATIONS
@@ -139,24 +154,17 @@
}
}
- public static void markKotlinMemberInfo(
- DexClass clazz, KotlinInfo kotlinInfo, Reporter reporter) {
+ static void markKotlinMemberInfo(DexClass clazz, KotlinInfo kotlinInfo, Reporter reporter) {
if (kotlinInfo == null || !kotlinInfo.hasDeclarations()) {
return;
}
- if (kotlinInfo.isClass()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asClass().kmClass, reporter);
- } else if (kotlinInfo.isFile()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asFile().kmPackage, reporter);
- } else if (kotlinInfo.isClassPart()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asClassPart().kmPackage, reporter);
- } else {
- throw new Unreachable("Unexpected KotlinInfo: " + kotlinInfo);
- }
- }
- private static void markKotlinMemberInfo(
- DexClass clazz, KmDeclarationContainer kmDeclarationContainer, Reporter reporter) {
+ KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations();
+ String companionObject = null;
+ if (kotlinInfo.isClass()) {
+ companionObject = kotlinInfo.asClass().kmClass.getCompanionObject();
+ }
+
Map<String, KmFunction> kmFunctionMap = new HashMap<>();
Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>();
Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>();
@@ -179,16 +187,26 @@
if (propertyProcessor.setterSignature() != null) {
kmPropertySetterMap.put(propertyProcessor.setterSignature().asString(), kmProperty);
}
- // TODO(b/70169921): property annotations
+ // TODO(b/151194869): property annotations
});
for (DexEncodedField field : clazz.fields()) {
+ if (companionObject != null && companionObject.equals(field.field.name.toString())) {
+ assert kotlinInfo.isClass();
+ kotlinInfo.asClass().foundCompanionObject(field);
+ continue;
+ }
String key = toJvmFieldSignature(field.field).asString();
if (kmPropertyFieldMap.containsKey(key)) {
KmProperty kmProperty = kmPropertyFieldMap.get(key);
field.setKotlinMemberInfo(
new KotlinMemberInfo(
- MemberKind.PROPERTY_BACKING_FIELD, kmProperty.getFlags(), kmProperty.getName()));
+ clazz == kotlinInfo.clazz
+ ? MemberKind.PROPERTY_BACKING_FIELD
+ : MemberKind.COMPANION_OBJECT_BACKING_FIELD,
+ kmProperty.getFlags(),
+ kmProperty.getFlags(),
+ kmProperty.getName()));
}
}
@@ -218,12 +236,16 @@
method.setKotlinMemberInfo(
new KotlinMemberInfo(
MemberKind.EXTENSION_PROPERTY_GETTER,
+ kmProperty.getGetterFlags(),
kmProperty.getFlags(),
kmProperty.getName()));
} else {
method.setKotlinMemberInfo(
new KotlinMemberInfo(
- MemberKind.PROPERTY_GETTER, kmProperty.getFlags(), kmProperty.getName()));
+ MemberKind.PROPERTY_GETTER,
+ kmProperty.getGetterFlags(),
+ kmProperty.getFlags(),
+ kmProperty.getName()));
}
} else if (kmPropertySetterMap.containsKey(key)) {
KmProperty kmProperty = kmPropertySetterMap.get(key);
@@ -231,6 +253,7 @@
method.setKotlinMemberInfo(
new KotlinMemberInfo(
MemberKind.EXTENSION_PROPERTY_SETTER,
+ kmProperty.getSetterFlags(),
kmProperty.getFlags(),
kmProperty.getName(),
kmProperty.getSetterParameter()));
@@ -238,6 +261,7 @@
method.setKotlinMemberInfo(
new KotlinMemberInfo(
MemberKind.PROPERTY_SETTER,
+ kmProperty.getSetterFlags(),
kmProperty.getFlags(),
kmProperty.getName(),
kmProperty.getSetterParameter()));
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
index fadf9cb..474c123 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -87,6 +87,7 @@
i -> addKotlinPrefix("jvm/functions/Function" + i + ";"))))
.build();
+ // TODO(b/151195430): remove backward type conversions.
private static String remapKotlinType(String type) {
if (knownTypeConversion.containsKey(type)) {
return knownTypeConversion.get(type);
@@ -94,6 +95,7 @@
return type;
}
+ // TODO(b/151195430): remove backward type conversions.
// Kotlin @Metadata deserialization has plain "kotlin", which will be relocated in r8lib.
// See b/70169921#comment57 for more details.
// E.g., desc: (Labc/xyz/C;Lkotlin/Function1;)kotlin/Unit
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 9507b32..5d1777f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -69,7 +69,7 @@
// If @Metadata is still associated, this class should not be renamed
// (by {@link ClassNameMinifier} of course).
// Or, we start maintaining @Metadata for renamed classes.
- // TODO(b/70169921): if this option is settled down, this assertion is meaningless.
+ // TODO(b/151194540): if this option is settled down, this assertion is meaningless.
assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
|| appView.options().enableKotlinMetadataRewritingForRenamedClasses
: clazz.toSourceString()
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index ce685b2..43b5cf2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
+import static kotlinx.metadata.Flag.Property.IS_VAR;
import static kotlinx.metadata.FlagsKt.flagsOf;
import com.android.tools.r8.graph.AppView;
@@ -115,11 +116,11 @@
if (classifier == null) {
return null;
}
- // TODO(b/70169921): Mysterious, why attempts to properly set flags bothers kotlinc?
+ // TODO(b/151194869): Mysterious, why attempts to properly set flags bothers kotlinc?
// and/or why wiping out flags works for KmType but not KmFunction?!
KmType kmType = new KmType(flagsOf());
kmType.visitClass(classifier);
- // TODO(b/70169921): Can be generalized too, like ArrayTypeSignature.Converter ?
+ // TODO(b/151194164): Can be generalized too, like ArrayTypeSignature.Converter ?
// E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
if (type.isArrayType() && !type.isPrimitiveArrayType()) {
DexType elementType = type.toArrayElementType(appView.dexItemFactory());
@@ -176,7 +177,7 @@
KmType argumentType = typeArgument.asClassTypeSignature().convert(this);
result.getArguments().add(new KmTypeProjection(KmVariance.INVARIANT, argumentType));
}
- // TODO(b/70169921): for TypeVariableSignature, there is KmType::visitTypeParameter.
+ // TODO(b/151194164): for TypeVariableSignature, there is KmType::visitTypeParameter.
return result;
}
@@ -234,11 +235,11 @@
// For a library method override, we should not have renamed it.
assert !method.isLibraryMethodOverride().isTrue() || renamedMethod.name == method.method.name
: method.toSourceString() + " -> " + renamedMethod.toSourceString();
- // TODO(b/70169921): Should we keep kotlin-specific flags only while synthesizing the base
+ // TODO(b/151194869): Should we keep kotlin-specific flags only while synthesizing the base
// value from general JVM flags?
int flag =
appView.appInfo().isPinned(method.method) && method.getKotlinMemberInfo() != null
- ? method.getKotlinMemberInfo().flag
+ ? method.getKotlinMemberInfo().flags
: method.accessFlags.getAsKotlinFlags();
KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
@@ -369,49 +370,57 @@
* getter, and so on.
*/
static class KmPropertyGroup {
- final int flag;
+ final int flags;
final String name;
final DexEncodedField field;
final DexEncodedMethod getter;
+ final int getterFlags;
final DexEncodedMethod setter;
+ final int setterFlags;
final DexEncodedMethod annotations;
final boolean isExtension;
private KmPropertyGroup(
- int flag,
+ int flags,
String name,
DexEncodedField field,
DexEncodedMethod getter,
+ int getterFlags,
DexEncodedMethod setter,
+ int setterFlags,
DexEncodedMethod annotations,
boolean isExtension) {
- this.flag = flag;
+ this.flags = flags;
this.name = name;
this.field = field;
this.getter = getter;
+ this.getterFlags = getterFlags;
this.setter = setter;
+ this.setterFlags = setterFlags;
this.annotations = annotations;
this.isExtension = isExtension;
}
- static Builder builder(int flag, String name) {
- return new Builder(flag, name);
+ static Builder builder(int flags, String name) {
+ return new Builder(flags, name);
}
static class Builder {
- private final int flag;
+ private final int flags;
private final String name;
private DexEncodedField field;
private DexEncodedMethod getter;
+ private int getterFlags;
private DexEncodedMethod setter;
+ private int setterFlags;
private DexEncodedMethod annotations;
private boolean isExtensionGetter;
private boolean isExtensionSetter;
private boolean isExtensionAnnotations;
- private Builder(int flag, String name) {
- this.flag = flag;
+ private Builder(int flags, String name) {
+ this.flags = flags;
this.name = name;
}
@@ -420,13 +429,15 @@
return this;
}
- Builder foundGetter(DexEncodedMethod getter) {
+ Builder foundGetter(DexEncodedMethod getter, int flags) {
this.getter = getter;
+ this.getterFlags = flags;
return this;
}
- Builder foundSetter(DexEncodedMethod setter) {
+ Builder foundSetter(DexEncodedMethod setter, int flags) {
this.setter = setter;
+ this.setterFlags = flags;
return this;
}
@@ -464,12 +475,13 @@
return null;
}
}
- return new KmPropertyGroup(flag, name, field, getter, setter, annotations, isExtension);
+ return new KmPropertyGroup(
+ flags, name, field, getter, getterFlags, setter, setterFlags, annotations, isExtension);
}
}
KmProperty toRenamedKmProperty(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- KmProperty kmProperty = new KmProperty(flag, name, flagsOf(), flagsOf());
+ KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
KmType kmPropertyType = null;
KmType kmReceiverType = null;
@@ -540,8 +552,13 @@
&& renamedPropertyName.equals(name)) {
renamedPropertyName = renamedGetter.name.toString();
}
- kmProperty.setGetterFlags(getter.accessFlags.getAsKotlinFlags());
+ kmProperty.setGetterFlags(getterFlags);
JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
+ } else if (field != null) {
+ // Property without getter.
+ // Even though a getter does not exist, `kotlinc` still set getter flags and use them to
+ // determine when to direct field access v.s. getter calls for property resolution.
+ kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
}
criteria = checkSetterCriteria();
@@ -610,8 +627,13 @@
&& renamedPropertyName.equals(name)) {
renamedPropertyName = renamedSetter.name.toString();
}
- kmProperty.setSetterFlags(setter.accessFlags.getAsKotlinFlags());
+ kmProperty.setSetterFlags(setterFlags);
JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
+ } else if (field != null && IS_VAR.invoke(flags)) {
+ // Editable property without setter.
+ // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
+ // determine when to direct field access v.s. setter calls for property resolution.
+ kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
}
// If the property type remains null at the end, bail out to synthesize this property.
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
index 5cc7d2e..1bb17ea 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
@@ -12,7 +12,7 @@
import kotlinx.metadata.jvm.KotlinClassMetadata;
public final class KotlinSyntheticClass extends KotlinInfo<KotlinClassMetadata.SyntheticClass> {
- // TODO(b/70169921): Once converted to internal data structure, this can be gone.
+ // TODO(b/151194794): Once converted to internal data structure, this can be gone.
private KotlinClassMetadata.SyntheticClass metadata;
public enum Flavour {
@@ -47,13 +47,13 @@
void processMetadata(KotlinClassMetadata.SyntheticClass metadata) {
this.metadata = metadata;
if (metadata.isLambda()) {
- // TODO(b/70169921): Use #toKmLambda to store a mutable model if needed.
+ // TODO(b/151194794): Use #toKmLambda to store a mutable model if needed.
}
}
@Override
void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- // TODO(b/70169921): no idea yet!
+ // TODO(b/151194794): no idea yet!
assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
|| appView.options().enableKotlinMetadataRewritingForRenamedClasses
: toString();
@@ -61,7 +61,7 @@
@Override
KotlinClassHeader createHeader() {
- // TODO(b/70169921): may need to update if `rewrite` is implemented.
+ // TODO(b/151194794): may need to update if `rewrite` is implemented.
return metadata.getHeader();
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index 0fa9f39..3134b8a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -12,14 +12,14 @@
// Provides access to Kotlin information about value parameter.
class KotlinValueParameterInfo {
- // TODO(b/70169921): When to use original param name v.s. when to *not* use?
+ // TODO(b/151193860): When to use original param name v.s. when to *not* use?
// Original parameter name.
final String name;
// Original parameter flag, e.g., has default value.
final int flag;
// Indicates whether the formal parameter is originally `vararg`.
final boolean isVararg;
- // TODO(b/70169921): Should we treat them as normal annotations? E.g., shrinking and renaming?
+ // TODO(b/151194869): Should we treat them as normal annotations? E.g., shrinking and renaming?
// Annotations on the type of value parameter.
final List<KmAnnotation> annotations;
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index 8f4330c..a5ead29 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -29,17 +29,26 @@
public void run() {
ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration();
String renameSourceFile = proguardConfiguration.getRenameSourceFileAttribute();
+ boolean hasRenameSourceFileAttribute = renameSourceFile != null;
// Return early if a user wants to keep the current source file attribute as-is.
- if (renameSourceFile == null && proguardConfiguration.getKeepAttributes().sourceFile) {
+ if (!hasRenameSourceFileAttribute
+ && proguardConfiguration.getKeepAttributes().sourceFile
+ && appView.options().forceProguardCompatibility) {
return;
}
- // Now, the user wants either to remove source file attribute or to rename it.
- DexString dexRenameSourceFile =
- renameSourceFile == null
- ? appView.dexItemFactory().createString("")
- : appView.dexItemFactory().createString(renameSourceFile);
+ // Now, the user wants either to remove source file attribute or to rename it for non-kept
+ // classes.
+ DexString defaultRenaming = getSourceFileRenaming(proguardConfiguration);
for (DexClass clazz : appView.appInfo().classes()) {
- clazz.sourceFile = dexRenameSourceFile;
+ // We only parse sourceFile if -keepattributes SourceFile, but for compat we should add
+ // a source file name, otherwise line positions will not be printed on the JVM or old version
+ // of ART.
+ if (!hasRenameSourceFileAttribute
+ && proguardConfiguration.getKeepAttributes().sourceFile
+ && appView.rootSet().mayNotBeMinified(clazz.type, appView)) {
+ continue;
+ }
+ clazz.sourceFile = defaultRenaming;
clazz.forEachMethod(encodedMethod -> {
// Abstract methods do not have code_item.
if (encodedMethod.shouldNotHaveCode()) {
@@ -66,4 +75,24 @@
});
}
}
+
+ private DexString getSourceFileRenaming(ProguardConfiguration proguardConfiguration) {
+ // If we should not be keeping the source file, null it out.
+ if (!appView.options().forceProguardCompatibility
+ && !proguardConfiguration.getKeepAttributes().sourceFile) {
+ return null;
+ }
+
+ String renamedSourceFileAttribute = proguardConfiguration.getRenameSourceFileAttribute();
+ if (renamedSourceFileAttribute != null) {
+ return appView.dexItemFactory().createString(renamedSourceFileAttribute);
+ }
+
+ // Otherwise, take the smallest size depending on platform. We cannot use NULL since the jvm
+ // and art will write at foo.bar.baz(Unknown Source) without a line-number. Newer version of ART
+ // will report the DEX PC.
+ return appView
+ .dexItemFactory()
+ .createString(appView.options().isGeneratingClassFiles() ? "SourceFile" : "");
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index b319c6d..4816759 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -142,9 +142,7 @@
lenseBuilder.add(encodedMethod.method);
accessFlags.promoteToFinal();
accessFlags.promoteToPublic();
- // Although the current method became public, it surely has the single virtual target.
- encodedMethod.method.setSingleVirtualMethodCache(
- encodedMethod.method.holder, encodedMethod);
+ // The method just became public and is therefore not a library override.
encodedMethod.setLibraryMethodOverride(OptionalBool.FALSE);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
index 8a65562..292f616 100644
--- a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
+++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -43,6 +43,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return invalid();
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
return setTarget(method, InvokeKind.VIRTUAL);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index 89e7c9d..c63a707 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -421,29 +421,29 @@
List<StackTraceLine> lines,
String classLoaderName) {
ClassReference classReference = Reference.classFromTypeName(clazz);
- retraceBase
- .retrace(classReference)
- .lookupMethod(method)
- .narrowByLine(linePosition)
- .forEach(
- methodElement -> {
- MethodReference methodReference = methodElement.getMethodReference();
- lines.add(
- new AtLine(
- startingWhitespace,
- at,
- classLoaderName,
- moduleName,
- methodReference.getHolderClass().getTypeName(),
- methodReference.getMethodName(),
- methodDescriptionFromMethodReference(methodReference, verbose),
- retraceBase.retraceSourceFile(
- classReference, fileName, methodReference.getHolderClass(), true),
- hasLinePosition()
- ? methodElement.getOriginalLineNumber(linePosition)
- : linePosition,
- methodElement.getRetraceMethodResult().isAmbiguous()));
- });
+ RetraceMethodResult retraceResult = retraceBase.retrace(classReference).lookupMethod(method);
+ if (linePosition != NO_POSITION && linePosition != INVALID_POSITION) {
+ retraceResult = retraceResult.narrowByLine(linePosition);
+ }
+ retraceResult.forEach(
+ methodElement -> {
+ MethodReference methodReference = methodElement.getMethodReference();
+ lines.add(
+ new AtLine(
+ startingWhitespace,
+ at,
+ classLoaderName,
+ moduleName,
+ methodReference.getHolderClass().getTypeName(),
+ methodReference.getMethodName(),
+ methodDescriptionFromMethodReference(methodReference, verbose),
+ retraceBase.retraceSourceFile(
+ classReference, fileName, methodReference.getHolderClass(), true),
+ hasLinePosition()
+ ? methodElement.getOriginalLineNumber(linePosition)
+ : linePosition,
+ methodElement.getRetraceMethodResult().isAmbiguous()));
+ });
}
@Override
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 44367e0..7df2fbc 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
@@ -165,6 +166,11 @@
/** Set of const-class references. */
public final Set<DexType> constClassReferences;
/**
+ * A map from seen init-class references to the minimum required visibility of the corresponding
+ * static field.
+ */
+ public final Map<DexType, Visibility> initClassReferences;
+ /**
* All methods and fields whose value *must* never be propagated due to a configuration directive.
* (testing only).
*/
@@ -184,6 +190,9 @@
final Set<DexType> instantiatedLambdas;
+ /* A cache to improve the lookup performance of lookupSingleVirtualTarget */
+ private final SingleTargetLookupCache singleTargetLookupCache = new SingleTargetLookupCache();
+
// TODO(zerny): Clean up the constructors so we have just one.
AppInfoWithLiveness(
DirectMappedDexApplication application,
@@ -225,7 +234,8 @@
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
EnumValueInfoMapCollection enumValueInfoMaps,
Set<DexType> instantiatedLambdas,
- Set<DexType> constClassReferences) {
+ Set<DexType> constClassReferences,
+ Map<DexType, Visibility> initClassReferences) {
super(application);
this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
@@ -266,6 +276,7 @@
this.enumValueInfoMaps = enumValueInfoMaps;
this.instantiatedLambdas = instantiatedLambdas;
this.constClassReferences = constClassReferences;
+ this.initClassReferences = initClassReferences;
}
public AppInfoWithLiveness(
@@ -308,7 +319,8 @@
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
EnumValueInfoMapCollection enumValueInfoMaps,
Set<DexType> instantiatedLambdas,
- Set<DexType> constClassReferences) {
+ Set<DexType> constClassReferences,
+ Map<DexType, Visibility> initClassReferences) {
super(appInfoWithSubtyping);
this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
@@ -349,6 +361,7 @@
this.enumValueInfoMaps = enumValueInfoMaps;
this.instantiatedLambdas = instantiatedLambdas;
this.constClassReferences = constClassReferences;
+ this.initClassReferences = initClassReferences;
}
private AppInfoWithLiveness(AppInfoWithLiveness previous) {
@@ -392,7 +405,8 @@
previous.switchMaps,
previous.enumValueInfoMaps,
previous.instantiatedLambdas,
- previous.constClassReferences);
+ previous.constClassReferences,
+ previous.initClassReferences);
copyMetadataFromPrevious(previous);
}
@@ -445,7 +459,8 @@
previous.switchMaps,
previous.enumValueInfoMaps,
previous.instantiatedLambdas,
- previous.constClassReferences);
+ previous.constClassReferences,
+ previous.initClassReferences);
copyMetadataFromPrevious(previous);
assert removedClasses == null || assertNoItemRemoved(previous.pinnedItems, removedClasses);
}
@@ -494,9 +509,11 @@
this.switchMaps = switchMaps;
this.enumValueInfoMaps = enumValueInfoMaps;
this.constClassReferences = previous.constClassReferences;
+ this.initClassReferences = previous.initClassReferences;
previous.markObsolete();
}
+ // TODO(b/150736225): Don't disable this assert.
private boolean dontAssertDefinitionFor = true;
public static AppInfoWithLivenessModifier modifier() {
@@ -504,16 +521,6 @@
}
@Override
- public void enableDefinitionForAssert() {
- dontAssertDefinitionFor = false;
- }
-
- @Override
- public void disableDefinitionForAssert() {
- dontAssertDefinitionFor = true;
- }
-
- @Override
public DexClass definitionFor(DexType type) {
DexClass definition = super.definitionFor(type);
assert dontAssertDefinitionFor
@@ -732,6 +739,10 @@
return objectAllocationInfoCollection;
}
+ void removeFromSingleTargetLookupCache(DexClass clazz) {
+ singleTargetLookupCache.removeInstantiatedType(clazz.type, this);
+ }
+
private boolean assertNoItemRemoved(Collection<DexReference> items, Collection<DexType> types) {
Set<DexType> typeSet = ImmutableSet.copyOf(types);
for (DexReference item : items) {
@@ -1069,7 +1080,8 @@
rewriteReferenceKeys(switchMaps, lens::lookupField),
enumValueInfoMaps.rewrittenWithLens(lens),
rewriteItems(instantiatedLambdas, lens::lookupType),
- constClassReferences);
+ rewriteItems(constClassReferences, lens::lookupType),
+ rewriteReferenceKeys(initClassReferences, lens::lookupType));
}
/**
@@ -1112,31 +1124,6 @@
}
}
- private DexEncodedMethod validateSingleVirtualTarget(
- DexEncodedMethod singleTarget, DexEncodedMethod resolutionResult) {
- assert resolutionResult.isVirtualMethod();
-
- if (singleTarget == null || singleTarget == DexEncodedMethod.SENTINEL) {
- return null;
- }
-
- // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception
- // at runtime.
- if (isInvalidSingleVirtualTarget(singleTarget, resolutionResult)) {
- return null;
- }
-
- return singleTarget;
- }
-
- private boolean isInvalidSingleVirtualTarget(
- DexEncodedMethod singleTarget, DexEncodedMethod resolutionResult) {
- assert resolutionResult.isVirtualMethod();
- // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception
- // at runtime.
- return !singleTarget.accessFlags.isAtLeastAsVisibleAs(resolutionResult.accessFlags);
- }
-
/** For mapping invoke virtual instruction to single target method. */
public DexEncodedMethod lookupSingleVirtualTarget(
DexMethod method, DexType invocationContext, boolean isInterface) {
@@ -1174,25 +1161,87 @@
// (it is either primitive or array).
return null;
}
+ DexClass initialResolutionHolder = definitionFor(method.holder);
+ if (initialResolutionHolder == null || initialResolutionHolder.isInterface() != isInterface) {
+ return null;
+ }
DexClass refinedReceiverClass = definitionFor(refinedReceiverType);
if (refinedReceiverClass == null) {
// The refined receiver is not defined in the program and we cannot determine the target.
return null;
}
+ if (receiverLowerBoundType == null
+ && singleTargetLookupCache.hasCachedItem(refinedReceiverType, method)) {
+ DexEncodedMethod cachedItem =
+ singleTargetLookupCache.getCachedItem(refinedReceiverType, method);
+ return cachedItem;
+ }
SingleResolutionResult resolution =
- resolveMethod(method.holder, method, isInterface).asSingleResolution();
+ resolveMethod(initialResolutionHolder, method).asSingleResolution();
if (resolution == null
|| !resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
return null;
}
// If the method is modeled, return the resolution.
+ DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
if (modeledPredicate.isModeled(resolution.getResolvedHolder().type)) {
if (resolution.getResolvedHolder().isFinal()
- || (resolution.getResolvedMethod().isFinal()
- && resolution.getResolvedMethod().accessFlags.isPublic())) {
- return resolution.getResolvedMethod();
+ || (resolvedMethod.isFinal() && resolvedMethod.accessFlags.isPublic())) {
+ singleTargetLookupCache.addToCache(refinedReceiverType, method, resolvedMethod);
+ return resolvedMethod;
}
}
+ DexEncodedMethod exactTarget =
+ getMethodTargetFromExactRuntimeInformation(
+ refinedReceiverType, receiverLowerBoundType, resolution, refinedReceiverClass);
+ if (exactTarget != null) {
+ // We are not caching single targets here because the cache does not include the
+ // lower bound dimension.
+ return exactTarget == DexEncodedMethod.SENTINEL ? null : exactTarget;
+ }
+ if (refinedReceiverClass.isNotProgramClass()) {
+ // The refined receiver is not defined in the program and we cannot determine the target.
+ singleTargetLookupCache.addToCache(refinedReceiverType, method, null);
+ return null;
+ }
+ DexClass resolvedHolder = resolution.getResolvedHolder();
+ // TODO(b/148769279): Disable lookup single target on lambda's for now.
+ if (resolvedHolder.isInterface()
+ && resolvedHolder.isProgramClass()
+ && hasAnyInstantiatedLambdas(resolvedHolder.asProgramClass())) {
+ singleTargetLookupCache.addToCache(refinedReceiverType, method, null);
+ return null;
+ }
+ DexEncodedMethod singleMethodTarget = null;
+ DexProgramClass refinedLowerBound = null;
+ if (receiverLowerBoundType != null) {
+ DexClass refinedLowerBoundClass = definitionFor(receiverLowerBoundType.getClassType());
+ if (refinedLowerBoundClass != null) {
+ refinedLowerBound = refinedLowerBoundClass.asProgramClass();
+ }
+ }
+ LookupResultSuccess lookupResult =
+ resolution
+ .lookupVirtualDispatchTargets(
+ invocationClass, this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
+ .asLookupResultSuccess();
+ if (lookupResult != null && !lookupResult.isIncomplete()) {
+ LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
+ if (singleTarget != null && singleTarget.isMethodTarget()) {
+ singleMethodTarget = singleTarget.asMethodTarget().getMethod();
+ }
+ }
+ if (receiverLowerBoundType == null) {
+ singleTargetLookupCache.addToCache(refinedReceiverType, method, singleMethodTarget);
+ }
+ return singleMethodTarget;
+ }
+
+ private DexEncodedMethod getMethodTargetFromExactRuntimeInformation(
+ DexType refinedReceiverType,
+ ClassTypeLatticeElement receiverLowerBoundType,
+ SingleResolutionResult resolution,
+ DexClass refinedReceiverClass) {
// If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
// runtime type information. In this case, the invoke will dispatch to the resolution result
// from the runtime type of the receiver.
@@ -1203,7 +1252,7 @@
resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this);
if (clazzAndMethod == null || isPinned(clazzAndMethod.getMethod().method)) {
// TODO(b/150640456): We should maybe only consider program methods.
- return null;
+ return DexEncodedMethod.SENTINEL;
}
return clazzAndMethod.getMethod();
} else {
@@ -1211,56 +1260,16 @@
// If we resolved to a method on the refined receiver in the library, then we report the
// method as a single target as well. This is a bit iffy since the library could change
// implementation, but we use this for library modelling.
- DexEncodedMethod targetOnReceiver = refinedReceiverClass.lookupVirtualMethod(method);
- if (targetOnReceiver != null
- && isOverriding(resolution.getResolvedMethod(), targetOnReceiver)) {
+ DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
+ DexEncodedMethod targetOnReceiver =
+ refinedReceiverClass.lookupVirtualMethod(resolvedMethod.method);
+ if (targetOnReceiver != null && isOverriding(resolvedMethod, targetOnReceiver)) {
return targetOnReceiver;
}
- return null;
+ return DexEncodedMethod.SENTINEL;
}
}
- if (refinedReceiverClass.isNotProgramClass()) {
- // The refined receiver is not defined in the program and we cannot determine the target.
- return null;
- }
- DexClass resolvedHolder = resolution.getResolvedHolder();
- // TODO(b/148769279): Disable lookup single target on lambda's for now.
- if (resolvedHolder.isInterface()
- && resolvedHolder.isProgramClass()
- && hasAnyInstantiatedLambdas(resolvedHolder.asProgramClass())) {
- return null;
- }
-
- if (method.isSingleVirtualMethodCached(refinedReceiverType)) {
- return method.getSingleVirtualMethodCache(refinedReceiverType);
- }
-
- DexProgramClass refinedLowerBound = null;
- if (receiverLowerBoundType != null) {
- assert receiverLowerBoundType.isClassType();
- DexClass refinedLowerBoundClass = definitionFor(receiverLowerBoundType.getClassType());
- if (refinedLowerBoundClass != null) {
- refinedLowerBound = refinedLowerBoundClass.asProgramClass();
- }
- }
-
- LookupResultSuccess lookupResult =
- resolution
- .lookupVirtualDispatchTargets(
- invocationClass, this, refinedReceiverClass.asProgramClass(), refinedLowerBound)
- .asLookupResultSuccess();
-
- if (lookupResult == null || lookupResult.isIncomplete()) {
- return null;
- }
-
- LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
- DexEncodedMethod singleMethodTarget = null;
- if (singleTarget != null && singleTarget.isMethodTarget()) {
- singleMethodTarget = singleTarget.asMethodTarget().getMethod();
- }
- method.setSingleVirtualMethodCache(refinedReceiverType, singleMethodTarget);
- return singleMethodTarget;
+ return null;
}
public AppInfoWithLiveness withSwitchMaps(Map<DexField, Int2ReferenceMap<DexField>> switchMaps) {
@@ -1275,12 +1284,6 @@
return new AppInfoWithLiveness(this, switchMaps, enumValueInfoMaps);
}
- public void forEachLiveProgramClass(Consumer<DexProgramClass> fn) {
- for (DexType type : liveTypes) {
- fn.accept(definitionFor(type).asProgramClass());
- }
- }
-
/**
* Visit all class definitions of classpath classes that are referenced in the compilation unit.
*
@@ -1363,14 +1366,32 @@
if (clazz == null) {
continue;
}
- if (isInstantiatedDirectly(clazz)
- || isPinned(clazz.type)
- || hasAnyInstantiatedLambdas(clazz)) {
+ if (isInstantiatedOrPinned(clazz)) {
subTypeConsumer.accept(clazz);
}
}
}
+ public void forEachInstantiatedSubTypeInChain(
+ DexProgramClass refinedReceiverUpperBound,
+ DexProgramClass refinedReceiverLowerBound,
+ Consumer<DexProgramClass> subTypeConsumer,
+ Consumer<LambdaDescriptor> callSiteConsumer) {
+ List<DexProgramClass> subTypes =
+ computeProgramClassRelationChain(refinedReceiverLowerBound, refinedReceiverUpperBound);
+ for (DexProgramClass subType : subTypes) {
+ if (isInstantiatedOrPinned(subType)) {
+ subTypeConsumer.accept(subType);
+ }
+ }
+ }
+
+ private boolean isInstantiatedOrPinned(DexProgramClass clazz) {
+ return isInstantiatedDirectly(clazz)
+ || isPinned(clazz.type)
+ || hasAnyInstantiatedLambdas(clazz);
+ }
+
public boolean isPinnedNotProgramOrLibraryOverride(DexReference reference) {
if (isPinned(reference)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
index 75c27fd..99f2d84 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLivenessModifier.java
@@ -36,8 +36,11 @@
// Instantiated classes.
ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection =
appInfo.getMutableObjectAllocationInfoCollection();
- noLongerInstantiatedClasses.forEach(objectAllocationInfoCollection::markNoLongerInstantiated);
-
+ noLongerInstantiatedClasses.forEach(
+ clazz -> {
+ objectAllocationInfoCollection.markNoLongerInstantiated(clazz);
+ appInfo.removeFromSingleTargetLookupCache(clazz);
+ });
// Written fields.
FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
appInfo.getMutableFieldAccessInfoCollection();
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
new file mode 100644
index 0000000..60bb9d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Visibility;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class ClassInitFieldSynthesizer {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexField clinitField;
+ private final InitClassLens.Builder lensBuilder = InitClassLens.builder();
+
+ public ClassInitFieldSynthesizer(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ this.clinitField = appView.dexItemFactory().objectMembers.clinitField;
+ }
+
+ public void run(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(
+ appView.appInfo().initClassReferences, this::synthesizeClassInitField, executorService);
+ appView.setInitClassLens(lensBuilder.build());
+ }
+
+ private void synthesizeClassInitField(DexType type, Visibility minimumRequiredVisibility) {
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
+ if (clazz == null) {
+ assert false;
+ return;
+ }
+ // Use an existing static field if there is one.
+ DexEncodedField encodedClinitField = null;
+ for (DexEncodedField staticField : clazz.staticFields()) {
+ // We need to field to be accessible from the contexts in which it is accessed.
+ if (!isMinimumRequiredVisibility(staticField, minimumRequiredVisibility)) {
+ continue;
+ }
+ // When compiling for dex, we can't use wide fields since we've only allocated a single
+ // register for the out-value of each ClassInit instruction
+ if (staticField.field.type.isWideType()) {
+ continue;
+ }
+ encodedClinitField = staticField;
+ break;
+ }
+ if (encodedClinitField == null) {
+ FieldAccessFlags accessFlags =
+ FieldAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC
+ | Constants.ACC_FINAL
+ | Constants.ACC_PUBLIC
+ | Constants.ACC_STATIC);
+ encodedClinitField =
+ new DexEncodedField(
+ appView.dexItemFactory().createField(clazz.type, clinitField.type, clinitField.name),
+ accessFlags,
+ DexAnnotationSet.empty(),
+ null);
+ clazz.appendStaticField(encodedClinitField);
+ }
+ lensBuilder.map(type, encodedClinitField.field);
+ }
+
+ private boolean isMinimumRequiredVisibility(
+ DexEncodedField field, Visibility minimumRequiredVisibility) {
+ if (field.isPublic()) {
+ return true;
+ }
+ switch (minimumRequiredVisibility) {
+ case PROTECTED:
+ return field.isProtected();
+ case PACKAGE_PRIVATE:
+ return field.isPackagePrivate() || field.isProtected();
+ case PUBLIC:
+ return false;
+ default:
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index f35fb94..db955c9 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -40,6 +40,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return enqueuer.traceInitClass(clazz, context);
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod invokedMethod) {
return enqueuer.traceInvokeVirtual(invokedMethod, context);
}
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 200562d..2bc230c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -97,6 +97,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
@@ -300,6 +301,12 @@
private final Set<DexType> constClassReferences = Sets.newIdentityHashSet();
/**
+ * A map from seen init-class references to the minimum required visibility of the corresponding
+ * static field.
+ */
+ private final Map<DexType, Visibility> initClassReferences = new IdentityHashMap<>();
+
+ /**
* A map from annotation classes to annotations that need to be processed should the classes ever
* become live.
*/
@@ -412,6 +419,9 @@
reportMissingClass(type);
return null;
}
+ if (clazz.isProgramClass()) {
+ return clazz;
+ }
if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
// TODO(b/149201735): This likely needs to apply to classpath too.
ensureMethodsContinueToWidenAccess(clazz);
@@ -727,6 +737,65 @@
return false;
}
+ boolean traceInitClass(DexType type, ProgramMethod currentMethod) {
+ assert type.isClassType();
+
+ Visibility oldMinimumRequiredVisibility = initClassReferences.get(type);
+ if (oldMinimumRequiredVisibility == null) {
+ DexProgramClass clazz = getProgramClassOrNull(type);
+ if (clazz == null) {
+ assert false;
+ return false;
+ }
+
+ initClassReferences.put(
+ type, computeMinimumRequiredVisibilityForInitClassField(type, currentMethod.getHolder()));
+
+ markTypeAsLive(type, classReferencedFromReporter(currentMethod.getMethod()));
+ markDirectAndIndirectClassInitializersAsLive(clazz);
+ return true;
+ }
+
+ if (oldMinimumRequiredVisibility.isPublic()) {
+ return false;
+ }
+
+ Visibility minimumRequiredVisibilityForCurrentMethod =
+ computeMinimumRequiredVisibilityForInitClassField(type, currentMethod.getHolder());
+
+ // There should never be a need to have an InitClass instruction for the enclosing class.
+ assert !minimumRequiredVisibilityForCurrentMethod.isPrivate();
+
+ if (minimumRequiredVisibilityForCurrentMethod.isPublic()) {
+ initClassReferences.put(type, minimumRequiredVisibilityForCurrentMethod);
+ return true;
+ }
+
+ if (oldMinimumRequiredVisibility.isProtected()) {
+ return false;
+ }
+
+ if (minimumRequiredVisibilityForCurrentMethod.isProtected()) {
+ initClassReferences.put(type, minimumRequiredVisibilityForCurrentMethod);
+ return true;
+ }
+
+ assert oldMinimumRequiredVisibility.isPackagePrivate();
+ assert minimumRequiredVisibilityForCurrentMethod.isPackagePrivate();
+ return false;
+ }
+
+ private Visibility computeMinimumRequiredVisibilityForInitClassField(
+ DexType clazz, DexProgramClass context) {
+ if (clazz.isSamePackage(context.type)) {
+ return Visibility.PACKAGE_PRIVATE;
+ }
+ if (appInfo.isStrictSubtypeOf(context.type, clazz)) {
+ return Visibility.PROTECTED;
+ }
+ return Visibility.PUBLIC;
+ }
+
void traceMethodHandle(
DexMethodHandle methodHandle, MethodHandleUse use, DexEncodedMethod currentMethod) {
// If a method handle is not an argument to a lambda metafactory it could flow to a
@@ -2555,7 +2624,8 @@
SetUtils.mapIdentityHashSet(
objectAllocationInfoCollection.unknownInstantiatedInterfaceTypes,
DexProgramClass::getType),
- constClassReferences);
+ constClassReferences,
+ initClassReferences);
appInfo.markObsolete();
return appInfoWithLiveness;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 3a6fcae..15d4e4b 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -100,6 +100,12 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ consumer.accept(clazz);
+ return true;
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
return registerInvoke(method);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
new file mode 100644
index 0000000..1691758
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class SingleTargetLookupCache {
+
+ private Map<DexType, Map<DexMethod, DexEncodedMethod>> cache = new ConcurrentHashMap<>();
+
+ public void addToCache(DexType refinedReceiverType, DexMethod method, DexEncodedMethod target) {
+ assert target != DexEncodedMethod.SENTINEL;
+ Map<DexMethod, DexEncodedMethod> methodCache =
+ cache.computeIfAbsent(refinedReceiverType, ignored -> new ConcurrentHashMap<>());
+ target = target == null ? DexEncodedMethod.SENTINEL : target;
+ assert methodCache.getOrDefault(method, target) == target;
+ methodCache.putIfAbsent(method, target);
+ }
+
+ public void removeInstantiatedType(DexType instantiatedType, AppInfoWithLiveness appInfo) {
+ // Remove all types in the hierarchy related to this type.
+ cache.remove(instantiatedType);
+ DexClass clazz = appInfo.definitionFor(instantiatedType);
+ if (clazz == null) {
+ return;
+ }
+ appInfo.forEachSuperType(clazz, (type, ignore) -> cache.remove(type));
+ appInfo.subtypes(instantiatedType).forEach(cache::remove);
+ }
+
+ public DexEncodedMethod getCachedItem(DexType receiverType, DexMethod method) {
+ Map<DexMethod, DexEncodedMethod> cachedMethods = cache.get(receiverType);
+ if (cachedMethods == null) {
+ return null;
+ }
+ DexEncodedMethod target = cachedMethods.get(method);
+ return target == DexEncodedMethod.SENTINEL ? null : target;
+ }
+
+ public boolean hasCachedItem(DexType receiverType, DexMethod method) {
+ Map<DexMethod, DexEncodedMethod> cachedMethods = cache.get(receiverType);
+ if (cachedMethods == null) {
+ return false;
+ }
+ return cachedMethods.containsKey(method);
+ }
+
+ public void clear() {
+ cache = new ConcurrentHashMap<>();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 43110c2..ef0ce63 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClass.FieldSetter;
-import com.android.tools.r8.graph.DexClass.MethodSetter;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -1452,8 +1451,7 @@
private VerticalClassMergerGraphLense fixupTypeReferences() {
// Globally substitute merged class types in protos and holders.
for (DexProgramClass clazz : appInfo.classes()) {
- fixupMethods(clazz.directMethods(), clazz::setDirectMethod);
- fixupMethods(clazz.virtualMethods(), clazz::setVirtualMethod);
+ clazz.getMethodCollection().replaceMethods(this::fixupMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
@@ -1467,21 +1465,16 @@
return lens;
}
- private void fixupMethods(List<DexEncodedMethod> methods, MethodSetter setter) {
- if (methods == null) {
- return;
- }
- for (int i = 0; i < methods.size(); i++) {
- DexEncodedMethod encodedMethod = methods.get(i);
- DexMethod method = encodedMethod.method;
- DexMethod newMethod = fixupMethod(method);
- if (newMethod != method) {
- if (!lensBuilder.hasOriginalSignatureMappingFor(newMethod)) {
- lensBuilder.map(method, newMethod).recordMove(method, newMethod);
- }
- setter.setMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
+ private DexEncodedMethod fixupMethod(DexEncodedMethod encodedMethod) {
+ DexMethod method = encodedMethod.method;
+ DexMethod newMethod = fixupMethod(method);
+ if (newMethod != method) {
+ if (!lensBuilder.hasOriginalSignatureMappingFor(newMethod)) {
+ lensBuilder.map(method, newMethod).recordMove(method, newMethod);
}
+ return encodedMethod.toTypeSubstitutedMethod(newMethod);
}
+ return encodedMethod;
}
private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
@@ -1837,6 +1830,11 @@
}
@Override
+ public boolean registerInitClass(DexType clazz) {
+ return checkTypeReference(clazz);
+ }
+
+ @Override
public boolean registerInvokeVirtual(DexMethod method) {
assert context != null;
GraphLenseLookupResult lookup =
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 51ab446..6aca533 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -371,6 +371,7 @@
return 'L' + className.replace(JAVA_PACKAGE_SEPARATOR, INNER_CLASS_SEPARATOR) + ';';
}
+ // TODO(b/151195430): Remove once a new version of kotlinx-metadata is released.
// Kotlin @Metadata deserialization has plain "kotlin", which will be relocated in r8lib.
// See b/70169921#comment25 for more details.
private static String backwardRelocatedName(String name) {
@@ -390,7 +391,7 @@
kmType.accept(new KmTypeVisitor() {
@Override
public void visitClass(String name) {
- // TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
+ // TODO(b/151195430): Remove this if metadata lib is resilient to namespace relocation.
// See b/70169921#comment25 for more details.
assert descriptor.get() == null : descriptor.get();
descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName(name)));
@@ -398,7 +399,7 @@
@Override
public void visitTypeAlias(String name) {
- // TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
+ // TODO(b/151195430): Remove this if metadata lib is resilient to namespace relocation.
// See b/70169921#comment25 for more details.
assert descriptor.get() == null : descriptor.get();
descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName(name)));
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 c3df10f..37b38e9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.optimize.Inliner;
@@ -58,6 +59,7 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
@@ -113,6 +115,8 @@
public DataResourceConsumer dataResourceConsumer;
public FeatureSplitConfiguration featureSplitConfiguration;
+ public List<Consumer<InspectorImpl>> outputInspections = Collections.emptyList();
+
// Constructor for testing and/or other utilities.
public InternalOptions() {
reporter = new Reporter();
@@ -227,6 +231,7 @@
public boolean applyInliningToInlinee =
System.getProperty("com.android.tools.r8.applyInliningToInlinee") != null;
public int applyInliningToInlineeMaxDepth = 0;
+ public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true;
public boolean enableInliningOfInvokesWithNullableReceivers = true;
public boolean disableInliningOfLibraryMethodOverrides = true;
public boolean enableClassInlining = true;
@@ -283,7 +288,7 @@
public boolean enablePcDebugInfoOutput = false;
// Number of threads to use while processing the dex files.
- public int numberOfThreads = DETERMINISTIC_DEBUGGING ? 1 : ThreadUtils.NOT_SPECIFIED;
+ public int threadCount = DETERMINISTIC_DEBUGGING ? 1 : ThreadUtils.NOT_SPECIFIED;
// Print smali disassembly.
public boolean useSmaliSyntax = false;
// Verbose output.
@@ -360,6 +365,10 @@
|| getProguardConfiguration().getKeepAttributes().stackMapTable;
}
+ public boolean shouldRerunEnqueuer() {
+ return isShrinking() || isMinifying() || getProguardConfiguration().hasApplyMappingFile();
+ }
+
public boolean isGeneratingDex() {
return isGeneratingDexIndexed() || isGeneratingDexFilePerClassFile();
}
@@ -1220,7 +1229,7 @@
}
public boolean canUseRequireNonNull() {
- return isGeneratingClassFiles() || hasMinApi(AndroidApiLevel.K);
+ return isGeneratingDex() && hasMinApi(AndroidApiLevel.K);
}
public boolean canUseSuppressedExceptions() {
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 64c44b5..ea35dfb 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -18,4 +18,8 @@
}
return -1;
}
+
+ public static <T> Iterable<T> filter(Iterable<T> methods, Predicate<T> predicate) {
+ return () -> IteratorUtils.filter(methods.iterator(), predicate);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java b/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
deleted file mode 100644
index 2458a64..0000000
--- a/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.utils;
-
-import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.graph.DexEncodedMember;
-import com.android.tools.r8.graph.DexMember;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-public class OrderedMergingIterator<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
- implements Iterator<D> {
-
- private final List<D> one;
- private final List<D> other;
- private int oneIndex = 0;
- private int otherIndex = 0;
-
- public OrderedMergingIterator(List<D> one, List<D> other) {
- this.one = one;
- this.other = other;
- }
-
- private D getNextChecked(List<D> list, int position) {
- if (position >= list.size()) {
- throw new NoSuchElementException();
- }
- return list.get(position);
- }
-
- @Override
- public boolean hasNext() {
- return oneIndex < one.size() || otherIndex < other.size();
- }
-
- @Override
- public D next() {
- if (oneIndex >= one.size()) {
- return getNextChecked(other, otherIndex++);
- }
- if (otherIndex >= other.size()) {
- return getNextChecked(one, oneIndex++);
- }
- int comparison = one.get(oneIndex).toReference().compareTo(other.get(otherIndex).toReference());
- if (comparison < 0) {
- return one.get(oneIndex++);
- }
- if (comparison == 0) {
- throw new InternalCompilerError("Source arrays are not disjoint.");
- }
- return other.get(otherIndex++);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 40232ca..84aa0d0 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -7,6 +7,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -39,6 +40,18 @@
executorService);
}
+ public static <T, U, E extends Exception> void processItems(
+ Map<T, U> items, ThrowingBiConsumer<T, U, E> consumer, ExecutorService executorService)
+ throws ExecutionException {
+ processItemsWithResults(
+ items.entrySet(),
+ arg -> {
+ consumer.accept(arg.getKey(), arg.getValue());
+ return null;
+ },
+ executorService);
+ }
+
public static void awaitFutures(Iterable<? extends Future<?>> futures)
throws ExecutionException {
Iterator<? extends Future<?>> futureIterator = futures.iterator();
@@ -90,18 +103,23 @@
static ExecutorService getExecutorServiceForProcessors(int processors) {
// This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
int threads = processors <= 2 ? processors : (int) Math.ceil(Integer.min(processors, 16) / 2.0);
+ return getExecutorServiceForThreads(threads);
+ }
+
+ static ExecutorService getExecutorServiceForThreads(int threads) {
+ // Note Executors.newSingleThreadExecutor() is not used when just one thread is used. See
+ // b/67338394.
return Executors.newWorkStealingPool(threads);
}
public static ExecutorService getExecutorService(int threads) {
- // Don't use Executors.newSingleThreadExecutor() when threads == 1, see b/67338394.
return threads == NOT_SPECIFIED
? getExecutorServiceForProcessors(Runtime.getRuntime().availableProcessors())
- : Executors.newWorkStealingPool(threads);
+ : getExecutorServiceForThreads(threads);
}
public static ExecutorService getExecutorService(InternalOptions options) {
- return getExecutorService(options.numberOfThreads);
+ return getExecutorService(options.threadCount);
}
public static int getNumberOfThreads(ExecutorService service) {
diff --git a/src/main/java/com/android/tools/r8/utils/Visibility.java b/src/main/java/com/android/tools/r8/utils/Visibility.java
new file mode 100644
index 0000000..32ec7ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/Visibility.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum Visibility {
+ PUBLIC,
+ PROTECTED,
+ PRIVATE,
+ PACKAGE_PRIVATE;
+
+ public boolean isPackagePrivate() {
+ return this == PACKAGE_PRIVATE;
+ }
+
+ public boolean isPrivate() {
+ return this == PRIVATE;
+ }
+
+ public boolean isProtected() {
+ return this == PROTECTED;
+ }
+
+ public boolean isPublic() {
+ return this == PUBLIC;
+ }
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case PUBLIC:
+ return "public";
+
+ case PROTECTED:
+ return "protected";
+
+ case PRIVATE:
+ return "private";
+
+ case PACKAGE_PRIVATE:
+ return "package-private";
+
+ default:
+ throw new Unreachable("Unexpected visibility");
+ }
+ }
+}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index b0c58c9..2450f6b 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -19,7 +19,7 @@
-keep public class com.android.tools.r8.Version { public static java.lang.String getPreReleaseString(); }
-keep public class com.android.tools.r8.Version { public static boolean isDevelopmentVersion(); }
--keepattributes LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
+-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
-keeppackagenames com.android.tools.r8
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 57fc2ec..8d43d07 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.sdklib.AndroidVersion;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
@@ -24,6 +25,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
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 java.io.IOException;
@@ -592,6 +594,32 @@
d8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(ThreadUtils.NOT_SPECIFIED, parse().getThreadCount());
+ assertEquals(1, parse("--thread-count", "1").getThreadCount());
+ assertEquals(2, parse("--thread-count", "2").getThreadCount());
+ assertEquals(10, parse("--thread-count", "10").getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains, handler -> parse(handler, "--thread-count", value));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 049cdb7..b62416f 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
@@ -60,6 +61,7 @@
DiagnosticsChecker handler = new DiagnosticsChecker();
try {
runner.run(handler);
+ fail("Failure expected");
} catch (CompilationFailedException e) {
checkContains(snippet, handler.errors);
throw e;
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 060bdbb..705ecff 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
@@ -14,6 +15,7 @@
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -257,7 +259,69 @@
AssertionTransformation.PASSTHROUGH);
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(
+ ThreadUtils.NOT_SPECIFIED,
+ parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 1,
+ parse(
+ "--thread-count",
+ "1",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 2,
+ parse(
+ "--thread-count",
+ "2",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 10,
+ parse(
+ "--thread-count",
+ "10",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains,
+ handler ->
+ parse(
+ handler,
+ "--thread-count",
+ value,
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString()));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private L8Command parse(String... args) throws CompilationFailedException {
return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
+
+ private L8Command parse(DiagnosticsHandler handler, String... args)
+ throws CompilationFailedException {
+ return L8Command.parse(args, EmbeddedOrigin.INSTANCE, handler).build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 4a703d7..29a9e22 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
@@ -20,6 +21,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
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 java.io.IOException;
@@ -709,6 +711,32 @@
r8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(ThreadUtils.NOT_SPECIFIED, parse().getThreadCount());
+ assertEquals(1, parse("--thread-count", "1").getThreadCount());
+ assertEquals(2, parse("--thread-count", "2").getThreadCount());
+ assertEquals(10, parse("--thread-count", "10").getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains, handler -> parse(handler, "--thread-count", value));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java
index 2ec28b8..9fe91d1 100644
--- a/src/test/java/com/android/tools/r8/R8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -59,6 +59,16 @@
return new CodeInspector(app, proguardMap);
}
+ @Override
+ public <E extends Throwable> R8TestRunResult inspectFailure(
+ ThrowingConsumer<CodeInspector, E> consumer) throws IOException, ExecutionException, E {
+ assertFailure();
+ assertNotNull(app);
+ CodeInspector codeInspector = new CodeInspector(app, proguardMap);
+ consumer.accept(codeInspector);
+ return self();
+ }
+
public <E extends Throwable> R8TestRunResult inspectOriginalStackTrace(
ThrowingConsumer<StackTrace, E> consumer) throws E {
consumer.accept(getOriginalStackTrace());
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 46bf0a1..bf3bb9d 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -142,6 +142,15 @@
return self();
}
+ public <E extends Throwable> RR inspectFailure(ThrowingConsumer<CodeInspector, E> consumer)
+ throws IOException, ExecutionException, E {
+ assertFailure();
+ assertNotNull(app);
+ CodeInspector inspector = new CodeInspector(app);
+ consumer.accept(inspector);
+ return self();
+ }
+
public <E extends Throwable> RR inspectStackTrace(ThrowingConsumer<StackTrace, E> consumer)
throws E {
consumer.accept(getStackTrace());
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 9c144c8..e184d97 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
@@ -248,6 +249,10 @@
this.shortName = shortName;
}
+ public boolean isDalvik() {
+ return isOlderThanOrEqual(Version.V4_4_4);
+ }
+
public boolean isLatest() {
return this == DEFAULT;
}
@@ -2075,6 +2080,7 @@
application,
null,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
options,
null);
diff --git a/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
index d59f954..e2ee52f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/InliningAfterStaticClassMergerTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,6 +36,7 @@
// Cannot be inlined into TestClass.main() because the static initialization of this class could
// have side-effects; in order for R8 to be conservative, library classes are treated as if
// their static initialization could have side-effects.
+ @NeverPropagateValue
public static String m() {
return "StaticMergeCandidateA.m()";
}
@@ -44,6 +46,7 @@
// Can be inlined into TestClass.main() because the static initialization of this class has no
// side-effects.
+ @NeverPropagateValue
public static String m() {
return "StaticMergeCandidateB.m()";
}
@@ -65,7 +68,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
@@ -82,8 +85,9 @@
.addKeepMainRule(TestClass.class)
.addOptionsModification(
options -> options.libraryInterfacesMayHaveStaticInitialization = true)
+ .enableMemberValuePropagationAnnotations()
.noMinification()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
@@ -94,11 +98,16 @@
.filter(clazz -> clazz.getOriginalName().contains("StaticMergeCandidate"))
.collect(Collectors.toList());
assertEquals(1, classes.size());
- assertEquals(StaticMergeCandidateA.class.getTypeName(), classes.get(0).getOriginalName());
- // Check that StaticMergeCandidateB.m() has not been moved into StaticMergeCandidateA, because
- // that would disable inlining of it.
- assertEquals(1, classes.get(0).allMethods().size());
+ FoundClassSubject clazz = classes.get(0);
+ assertEquals(StaticMergeCandidateA.class.getTypeName(), clazz.getOriginalName());
+
+ // Check that StaticMergeCandidateB.m() has been inlined.
+ assertEquals(0, clazz.allMethods().size());
+
+ // Check that a static field has been synthesized in order to trigger class initialization.
+ assertEquals(1, clazz.allStaticFields().size());
+ assertEquals("$r8$clinit", clazz.allStaticFields().get(0).getOriginalName());
// Check that the test class only has a main method.
ClassSubject classSubject = inspector.clazz(TestClass.class);
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index 6f2e729..005df04 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -44,11 +44,12 @@
private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
return computeAppViewWithLiveness(
- buildClasses(I.class, J.class, K.class, L.class, A.class).build(),
+ buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class).build(),
factory -> {
List<ProguardConfigurationRule> rules = new ArrayList<>();
rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
+ rules.addAll(buildKeepRuleForClassAndMethods(Main.class, factory));
return rules;
});
}
@@ -265,4 +266,11 @@
public interface L extends I, K {}
public static class A implements L {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index d1c3ac1..b42d7fa 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -153,7 +153,7 @@
checkLine(SOURCE_FILE, 15),
stepInto(INTELLIJ_FILTER),
checkMethod(innerClassName, innerMethodName, innerSignature),
- checkLine(SOURCE_FILE, 8),
+ checkLine(8),
run());
}
@@ -175,7 +175,7 @@
breakpoint(innerClassName, innerMethodName, innerSignature),
run(),
checkMethod(innerClassName, innerMethodName, innerSignature),
- checkLine(SOURCE_FILE, 8),
+ checkLine(8),
run());
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
index b344944..11dff7b 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DexPcWithDebugInfoForOverloadedMethodsTestRunner.java
@@ -23,12 +23,13 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceMethodResult;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.Matchers.InlinePosition;
+import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -41,6 +42,7 @@
@RunWith(Parameterized.class)
public class DexPcWithDebugInfoForOverloadedMethodsTestRunner extends TestBase {
+ private static final String FILENAME_INLINE = "InlineFunction.kt";
private static final Class<?> MAIN = DexPcWithDebugInfoForOverloadedMethodsTest.class;
private static final int MINIFIED_LINE_POSITION = 6;
private static final String EXPECTED = "java.lang.RuntimeException: overloaded(String)42";
@@ -66,6 +68,7 @@
.addKeepMainRule(MAIN)
.addKeepMethodRules(MAIN, "void overloaded(...)")
.addKeepAttributeLineNumberTable()
+ .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
.addOptionsModification(
internalOptions -> {
@@ -86,18 +89,20 @@
MethodSubject throwingSubject =
codeInspector.clazz(MAIN).method("void", "overloaded", "java.lang.String");
assertThat(throwingSubject, isPresent());
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
Reference.methodFromMethod(
MAIN.getDeclaredMethod("inlinee", String.class)),
MINIFIED_LINE_POSITION,
- 11),
- InlinePosition.create(
+ 11,
+ FILENAME_INLINE),
+ LinePosition.create(
Reference.methodFromMethod(
MAIN.getDeclaredMethod("overloaded", String.class)),
MINIFIED_LINE_POSITION,
- 20));
+ 20,
+ FILENAME_INLINE));
RetraceMethodResult retraceResult =
throwingSubject
.streamInstructions()
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index c47f16f..ae533a4 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -22,7 +22,7 @@
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.Matchers.InlinePosition;
+import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -35,6 +35,7 @@
@RunWith(Parameterized.class)
public class EnsureNoDebugInfoEmittedForPcOnlyTestRunner extends TestBase {
+ private static final String FILENAME_MAIN = "EnsureNoDebugInfoEmittedForPcOnlyTest.java";
private static final Class<?> MAIN = EnsureNoDebugInfoEmittedForPcOnlyTest.class;
private static final int INLINED_DEX_PC = 32;
@@ -75,20 +76,23 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(MAIN).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
Reference.methodFromMethod(MAIN.getDeclaredMethod("a")),
INLINED_DEX_PC,
- 11),
- InlinePosition.create(
+ 11,
+ FILENAME_MAIN),
+ LinePosition.create(
Reference.methodFromMethod(MAIN.getDeclaredMethod("b")),
INLINED_DEX_PC,
- 18),
- InlinePosition.create(
+ 18,
+ FILENAME_MAIN),
+ LinePosition.create(
mainSubject.asFoundMethodSubject().asMethodReference(),
INLINED_DEX_PC,
- 23));
+ 23,
+ FILENAME_MAIN));
RetraceMethodResult retraceResult =
mainSubject
.streamInstructions()
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 02623a8..b1a6811 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
@@ -5,7 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.r8ondex;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8;
import com.android.tools.r8.TestParameters;
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index 75ffea7..eeedbab 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import java.util.Collections;
@@ -21,6 +23,8 @@
return new ObjectToOffsetMapping(
DexApplication.builder(new InternalOptions(new DexItemFactory(), new Reporter()), null)
.build(),
+ NamingLens.getIdentityLens(),
+ InitClassLens.getDefault(),
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index 666cb87..945f3c2 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -25,7 +25,6 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -43,7 +42,6 @@
public void branching() {
DexItemFactory factory = new DexItemFactory();
DexString string = factory.createString("turn into jumbo");
- factory.sort(NamingLens.getIdentityLens());
Instruction[] instructions = buildInstructions(string, false);
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
@@ -60,7 +58,6 @@
public void branching2() {
DexItemFactory factory = new DexItemFactory();
DexString string = factory.createString("turn into jumbo");
- factory.sort(NamingLens.getIdentityLens());
Instruction[] instructions = buildInstructions(string, true);
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
@@ -142,7 +139,6 @@
DexItemFactory factory = inspector.getFactory();
DexString string = factory.createString("view must have a tag");
- factory.sort(NamingLens.getIdentityLens());
DexCode code = jumboStringProcess(factory, string, instructions);
Instruction[] rewrittenInstructions = code.instructions;
assertEquals(289, countJumboStrings(rewrittenInstructions));
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 8542a76..817abcd 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.naming.NamingLens;
@@ -155,6 +156,7 @@
options,
null,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null);
ExecutorService executorService = ThreadUtils.getExecutorService(options);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
new file mode 100644
index 0000000..7d6f2ac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingArrayTest.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EnumUnboxingArrayTest extends EnumUnboxingTestBase {
+
+ private static final Class<?>[] FAILURES = {
+ EnumVarArgs.class,
+ EnumArrayReadWriteNoEscape.class,
+ EnumArrayReadWrite.class,
+ Enum2DimArrayReadWrite.class
+ };
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final KeepRule enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public EnumUnboxingArrayTest(
+ TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxingFailure() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EnumUnboxingArrayTest.class)
+ .addKeepMainRules(FAILURES)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepRules(enumKeepRules.getKeepRule())
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ for (Class<?> failure : FAILURES) {
+ R8TestRunResult run =
+ compile
+ .inspectDiagnosticMessages(
+ m ->
+ assertEnumIsBoxed(
+ failure.getDeclaredClasses()[0], failure.getSimpleName(), m))
+ .run(parameters.getRuntime(), failure)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+ }
+
+ static class EnumVarArgs {
+
+ public static void main(String[] args) {
+ System.out.println(sum(MyEnum.A));
+ System.out.println(1);
+ System.out.println(sum(MyEnum.B, MyEnum.C));
+ System.out.println(2);
+ }
+
+ @NeverInline
+ public static int sum(MyEnum... args) {
+ return args.length;
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+ }
+
+ static class EnumArrayReadWriteNoEscape {
+
+ public static void main(String[] args) {
+ MyEnum[] myEnums = new MyEnum[2];
+ myEnums[1] = MyEnum.C;
+ System.out.println(myEnums[1].ordinal());
+ System.out.println(2);
+ System.out.println(myEnums[0]);
+ System.out.println("null");
+ myEnums[0] = MyEnum.B;
+ System.out.println(myEnums.length);
+ System.out.println(2);
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+ }
+
+ static class EnumArrayReadWrite {
+
+ public static void main(String[] args) {
+ MyEnum[] myEnums = getArray();
+ System.out.println(myEnums[1].ordinal());
+ System.out.println(2);
+ System.out.println(myEnums[0]);
+ System.out.println("null");
+ myEnums[0] = MyEnum.B;
+ System.out.println(sum(myEnums));
+ System.out.println(2);
+ }
+
+ @NeverInline
+ public static MyEnum[] getArray() {
+ MyEnum[] myEnums = new MyEnum[2];
+ myEnums[1] = MyEnum.C;
+ return myEnums;
+ }
+
+ @NeverInline
+ public static int sum(MyEnum[] args) {
+ return args.length;
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+ }
+
+ static class Enum2DimArrayReadWrite {
+
+ public static void main(String[] args) {
+ MyEnum[][] myEnums = getArray();
+ System.out.println(myEnums[1][1].ordinal());
+ System.out.println(2);
+ System.out.println(myEnums[0][0].ordinal());
+ System.out.println(1);
+ }
+
+ @NeverInline
+ public static MyEnum[][] getArray() {
+ MyEnum[][] myEnums = new MyEnum[2][2];
+ myEnums[1][1] = MyEnum.C;
+ myEnums[1][0] = MyEnum.A;
+ myEnums[0][0] = MyEnum.B;
+ myEnums[0][1] = MyEnum.A;
+ return myEnums;
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index 1b56b1b..e03d352 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -73,6 +73,7 @@
}
void assertEnumIsUnboxed(Class<?> enumClass, String testName, TestDiagnosticMessages m) {
+ assertTrue(enumClass.isEnum());
Diagnostic diagnostic = m.getInfos().get(0);
assertTrue(diagnostic.getDiagnosticMessage().startsWith("Unboxed enums"));
assertTrue(
@@ -81,6 +82,7 @@
}
void assertEnumIsBoxed(Class<?> enumClass, String testName, TestDiagnosticMessages m) {
+ assertTrue(enumClass.isEnum());
Diagnostic diagnostic = m.getInfos().get(1);
assertTrue(diagnostic.getDiagnosticMessage().startsWith("Boxed enums"));
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
index f2ed08b..eedc8f1 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
@@ -81,10 +81,12 @@
C
}
- MyEnum e;
+ MyEnum e = null;
public static void main(String[] args) {
InstanceFieldPut fieldPut = new InstanceFieldPut();
+ System.out.println(fieldPut.e == null);
+ System.out.println("true");
fieldPut.setA();
System.out.println(fieldPut.e.ordinal());
System.out.println(0);
@@ -113,9 +115,11 @@
C
}
- static MyEnum e;
+ static MyEnum e = null;
public static void main(String[] args) {
+ System.out.println(StaticFieldPut.e == null);
+ System.out.println("true");
setA();
System.out.println(StaticFieldPut.e.ordinal());
System.out.println(0);
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 775f030..33321eb 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -37,26 +37,30 @@
AndroidApp app =
testForD8()
.debug()
- .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+ .addProgramClassesAndInnerClasses(
+ GenericSignatureTestClassA.class,
+ GenericSignatureTestClassB.class,
+ GenericSignatureTestClassCY.class,
+ GenericSignatureTestClassCYY.class)
.compile()
.app;
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
DexItemFactory factory = appView.dexItemFactory();
CodeInspector inspector = new CodeInspector(appView.appInfo().app());
- ClassSubject a = inspector.clazz(A.class);
+ ClassSubject a = inspector.clazz(GenericSignatureTestClassA.class);
assertThat(a, isPresent());
- ClassSubject y = inspector.clazz(A.Y.class);
+ ClassSubject y = inspector.clazz(GenericSignatureTestClassA.Y.class);
assertThat(y, isPresent());
- ClassSubject yy = inspector.clazz(A.Y.YY.class);
+ ClassSubject yy = inspector.clazz(GenericSignatureTestClassA.Y.YY.class);
assertThat(yy, isPresent());
- ClassSubject zz = inspector.clazz(A.Y.ZZ.class);
+ ClassSubject zz = inspector.clazz(GenericSignatureTestClassA.Y.ZZ.class);
assertThat(zz, isPresent());
- ClassSubject b = inspector.clazz(B.class);
+ ClassSubject b = inspector.clazz(GenericSignatureTestClassB.class);
assertThat(b, isPresent());
- ClassSubject cy = inspector.clazz(CY.class);
+ ClassSubject cy = inspector.clazz(GenericSignatureTestClassCY.class);
assertThat(cy, isPresent());
- ClassSubject cyy = inspector.clazz(CYY.class);
+ ClassSubject cyy = inspector.clazz(GenericSignatureTestClassCYY.class);
assertThat(cyy, isPresent());
DexEncodedMethod method;
@@ -252,7 +256,7 @@
// and then extended a bit to explore more details, e.g., MethodTypeSignature.
//
-class A<T> {
+class GenericSignatureTestClassA<T> {
class Y {
class YY {}
@@ -260,7 +264,7 @@
class ZZ<TT> extends YY {
public YY yy;
- YY newYY(B... bs) {
+ YY newYY(GenericSignatureTestClassB... bs) {
return new YY();
}
@@ -301,8 +305,9 @@
}
}
-class B<T extends A<T>> {}
+class GenericSignatureTestClassB<T extends GenericSignatureTestClassA<T>> {}
-class CY<T extends A<T>.Y> {}
+class GenericSignatureTestClassCY<T extends GenericSignatureTestClassA<T>.Y> {}
-class CYY<T extends A<T>.Y> extends CY<T> {}
+class GenericSignatureTestClassCYY<T extends GenericSignatureTestClassA<T>.Y>
+ extends GenericSignatureTestClassCY<T> {}
diff --git a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
index 43015bf..9251ab8 100644
--- a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
@@ -30,7 +30,8 @@
@Parameters(name = "{1}, enabled: {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public InitializedClassesInInstanceMethodsTest(
@@ -53,7 +54,7 @@
.allowAccessModification()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
.run(parameters.getRuntime(), TestClass.class)
@@ -70,13 +71,15 @@
assertThat(outerClassSubject.uniqueMethodWithName("world"), not(isPresent()));
assertThat(outerClassSubject.uniqueMethodWithName("exclamationMark"), not(isPresent()));
- int numberOfExpectedAccessibilityBridges =
- enableInitializedClassesInInstanceMethodsAnalysis ? 0 : 3;
+ int numberOfExpectedAccessibilityBridges = 0;
assertEquals(
numberOfExpectedAccessibilityBridges,
outerClassSubject
.allMethods(method -> method.getOriginalName().contains("access$"))
.size());
+ assertEquals(
+ !enableInitializedClassesInInstanceMethodsAnalysis,
+ outerClassSubject.uniqueFieldWithName("$r8$clinit").isPresent());
ClassSubject aClassSubject = inspector.clazz(Outer.A.class);
assertThat(aClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/inspection/FieldFlagsAndValueInspectionTest.java b/src/test/java/com/android/tools/r8/inspection/FieldFlagsAndValueInspectionTest.java
new file mode 100644
index 0000000..d64be27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/inspection/FieldFlagsAndValueInspectionTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.inspector.Inspector;
+import com.android.tools.r8.inspector.ValueInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldFlagsAndValueInspectionTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("30");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public FieldFlagsAndValueInspectionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ return;
+ }
+ testForD8()
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(inspector -> inspection(inspector, false)))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(inspector -> inspection(inspector, true)))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ private int foundFields = 0;
+
+ private void inspection(Inspector inspector, boolean isR8) {
+ inspector.forEachClass(
+ classInspector -> {
+ classInspector.forEachField(
+ fieldInspector -> {
+ foundFields++;
+ String name = fieldInspector.getFieldReference().getFieldName();
+ assertEquals(name.contains("s"), fieldInspector.isStatic());
+ assertEquals(name.contains("f"), fieldInspector.isFinal());
+ assertEquals(Reference.INT, fieldInspector.getFieldReference().getFieldType());
+ Optional<ValueInspector> value = fieldInspector.getInitialValue();
+ if (fieldInspector.isStatic() && fieldInspector.isFinal()) {
+ // The static final 'sfi' is static initialized to 2.
+ assertTrue(value.isPresent());
+ assertEquals(2, value.get().asIntValue().getIntValue());
+ } else if (fieldInspector.isStatic()) {
+ // The static 'si' is default initialized to 0 and clinit sets it to 4.
+ // R8 optimizes that to directly set 4.
+ assertTrue(value.isPresent());
+ assertEquals(isR8 ? 4 : 0, value.get().asIntValue().getIntValue());
+ } else {
+ assertFalse(value.isPresent());
+ }
+ });
+ });
+ }
+
+ private void assertFound() {
+ assertEquals(4, foundFields);
+ }
+
+ static class TestClass {
+ public static final int sfi = 2;
+ public static int si = 4;
+ public final int fi = 8;
+ public int i = 16;
+
+ public static void main(String[] args) {
+ TestClass obj = new TestClass();
+ System.out.println(obj.sfi + obj.si + obj.fi + obj.i);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/inspection/FieldValueTypesInspectionTest.java b/src/test/java/com/android/tools/r8/inspection/FieldValueTypesInspectionTest.java
new file mode 100644
index 0000000..0031885
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/inspection/FieldValueTypesInspectionTest.java
@@ -0,0 +1,196 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspection;
+
+import static org.junit.Assert.assertEquals;
+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.inspector.Inspector;
+import com.android.tools.r8.inspector.ValueInspector;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+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 FieldValueTypesInspectionTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines(
+ "" + TestClass.z,
+ "" + TestClass.b,
+ "" + TestClass.c,
+ "" + TestClass.s,
+ "" + TestClass.i,
+ "" + TestClass.j,
+ "" + TestClass.f,
+ "" + TestClass.d,
+ "foo");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public FieldValueTypesInspectionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ return;
+ }
+ testForD8()
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(this::inspection))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(this::inspection))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ private int foundFields = 0;
+
+ private void inspection(Inspector inspector) {
+ inspector.forEachClass(
+ classInspector -> {
+ classInspector.forEachField(
+ fieldInspector -> {
+ foundFields++;
+ FieldReference reference = fieldInspector.getFieldReference();
+ ValueInspector value = fieldInspector.getInitialValue().get();
+ assertEquals(reference.getFieldType(), value.getTypeReference());
+ String name = reference.getFieldName();
+ boolean isBoolean = name.equals("z");
+ boolean isByte = name.equals("b");
+ boolean isChar = name.equals("c");
+ boolean isShort = name.equals("s");
+ boolean isInt = name.equals("i");
+ boolean isLong = name.equals("j");
+ boolean isFloat = name.equals("f");
+ boolean isDouble = name.equals("d");
+ boolean isString = name.equals("str");
+ assertEquals(isBoolean, value.isBooleanValue());
+ assertEquals(isByte, value.isByteValue());
+ assertEquals(isChar, value.isCharValue());
+ assertEquals(isShort, value.isShortValue());
+ assertEquals(isInt, value.isIntValue());
+ assertEquals(isLong, value.isLongValue());
+ assertEquals(isFloat, value.isFloatValue());
+ assertEquals(isDouble, value.isDoubleValue());
+ assertEquals(isString, value.isStringValue());
+ if (isBoolean) {
+ assertEquals(Reference.BOOL, reference.getFieldType());
+ assertEquals(TestClass.z, value.asBooleanValue().getBooleanValue());
+ } else {
+ assertNull(value.asBooleanValue());
+ }
+ if (isByte) {
+ assertEquals(Reference.BYTE, reference.getFieldType());
+ assertEquals(TestClass.b, value.asByteValue().getByteValue());
+ } else {
+ assertNull(value.asByteValue());
+ }
+ if (isChar) {
+ assertEquals(Reference.CHAR, reference.getFieldType());
+ assertEquals(TestClass.c, value.asCharValue().getCharValue());
+ } else {
+ assertNull(value.asCharValue());
+ }
+ if (isShort) {
+ assertEquals(Reference.SHORT, reference.getFieldType());
+ assertEquals(TestClass.s, value.asShortValue().getShortValue());
+ } else {
+ assertNull(value.asShortValue());
+ }
+ if (isInt) {
+ assertEquals(Reference.INT, reference.getFieldType());
+ assertEquals(TestClass.i, value.asIntValue().getIntValue());
+ } else {
+ assertNull(value.asIntValue());
+ }
+ if (isLong) {
+ assertEquals(Reference.LONG, reference.getFieldType());
+ assertEquals(TestClass.j, value.asLongValue().getLongValue());
+ } else {
+ assertNull(value.asLongValue());
+ }
+ if (isFloat) {
+ assertEquals(Reference.FLOAT, reference.getFieldType());
+ assertEquals(
+ Float.floatToRawIntBits(TestClass.f),
+ Float.floatToRawIntBits(value.asFloatValue().getFloatValue()));
+ } else {
+ assertNull(value.asFloatValue());
+ }
+ if (isDouble) {
+ assertEquals(Reference.DOUBLE, reference.getFieldType());
+ assertEquals(
+ Double.doubleToRawLongBits(TestClass.d),
+ Double.doubleToRawLongBits(value.asDoubleValue().getDoubleValue()));
+ } else {
+ assertNull(value.asDoubleValue());
+ }
+ if (isString) {
+ assertEquals(Reference.classFromClass(String.class), reference.getFieldType());
+ assertEquals(TestClass.str, value.asStringValue().getStringValue());
+ } else {
+ assertNull(value.asStringValue());
+ }
+ });
+ });
+ }
+
+ private void assertFound() {
+ assertEquals(9, foundFields);
+ }
+
+ static class TestClass {
+ public static final boolean z = true;
+ public static final byte b = 2;
+ public static final char c = 4;
+ public static final short s = 8;
+ public static final int i = 16;
+ public static final long j = 32L;
+ public static final float f = 64.1F;
+ public static final double d = 128.1D;
+ public static final String str = "foo";
+
+ public static void main(String[] args) {
+ System.out.println(z);
+ System.out.println(b);
+ System.out.println(c);
+ System.out.println(s);
+ System.out.println(i);
+ System.out.println(j);
+ System.out.println(f);
+ System.out.println(d);
+ System.out.println(str);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/inspection/InspectionApiTest.java b/src/test/java/com/android/tools/r8/inspection/InspectionApiTest.java
new file mode 100644
index 0000000..5c5a6e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/inspection/InspectionApiTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.inspection;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.inspector.Inspector;
+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.Reference;
+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 InspectionApiTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InspectionApiTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ return;
+ }
+ testForD8()
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(this::inspection))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> b.getBuilder().addOutputInspection(this::inspection))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ assertFound();
+ }
+
+ ClassReference foundClass = null;
+ FieldReference foundField = null;
+ MethodReference foundMethod = null;
+
+ private void inspection(Inspector inspector) {
+ inspector.forEachClass(
+ classInspector -> {
+ foundClass = classInspector.getClassReference();
+ classInspector.forEachField(
+ fieldInspector -> {
+ foundField = fieldInspector.getFieldReference();
+ });
+ classInspector.forEachMethod(
+ methodInspector -> {
+ // Ignore clinit (which is removed in R8).
+ if (!methodInspector.getMethodReference().getMethodName().equals("<clinit>")) {
+ foundMethod = methodInspector.getMethodReference();
+ }
+ });
+ });
+ }
+
+ private void assertFound() throws Exception {
+ assertEquals(Reference.classFromClass(TestClass.class), foundClass);
+ assertEquals(Reference.fieldFromField(TestClass.class.getDeclaredField("foo")), foundField);
+ assertEquals(
+ Reference.methodFromMethod(TestClass.class.getDeclaredMethod("main", String[].class)),
+ foundMethod);
+ }
+
+ static class TestClass {
+ public static int foo = 42;
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index a16962d..cba792d 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -41,7 +41,7 @@
// For this test just do random shuffle.
options.testing.irOrdering = NondeterministicIROrdering.getInstance();
// Only use one thread to process to process in the order decided by the callback.
- options.numberOfThreads = 1;
+ options.threadCount = 1;
// Ignore the missing classes.
options.ignoreMissingClasses = true;
// Store the generated Proguard map.
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
index 48acfdb..97b4db0 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -13,6 +14,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.analysis.ProtoApplicationStats;
import org.junit.Test;
@@ -29,7 +31,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters().withNoneRuntime().build();
}
public YouTubeV1419TreeShakeJarVerificationTest(TestParameters parameters) {
@@ -43,11 +45,21 @@
assumeTrue(isLocalDevelopment());
assumeTrue(shouldRunSlowTests());
+ LibrarySanitizer librarySanitizer =
+ new LibrarySanitizer(temp).addProguardConfigurationFiles(getKeepRuleFiles()).sanitize();
+
R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addKeepRuleFiles(getKeepRuleFiles())
+ testForR8(Backend.DEX)
+ .addKeepRuleFiles(librarySanitizer.getSanitizedProguardConfiguration())
+ .addLibraryFiles(librarySanitizer.getSanitizedLibrary())
+ .addMainDexRuleFiles(getMainDexRuleFiles())
+ .allowDiagnosticMessages()
.allowUnusedProguardConfigurationRules()
- .compile();
+ .setMinApi(AndroidApiLevel.H_MR2)
+ .compile()
+ .assertAllInfoMessagesMatch(
+ containsString("Proguard configuration rule does not match anything"))
+ .assertAllWarningMessagesMatch(containsString("Ignoring option:"));
if (ToolHelper.isLocalDevelopment()) {
DexItemFactory dexItemFactory = new DexItemFactory();
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 8d73fad..3b3473a 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -44,7 +44,6 @@
protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
try {
- options.itemFactory.resetSortedIndices();
return new ApplicationReader(input, options, Timing.empty()).read();
} catch (IOException | ExecutionException e) {
throw new RuntimeException(e);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCannotBePostponedTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCannotBePostponedTest.java
index d29753b..7bc227a 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCannotBePostponedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCannotBePostponedTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.sideeffect;
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;
@@ -47,9 +48,9 @@
ClassSubject classSubject = inspector.clazz(A.class);
assertThat(classSubject, isPresent());
- // A.inlineable() cannot be inlined because it should trigger the class initialization of A,
- // which should trigger the class initialization of B, which will print "Hello".
- assertThat(classSubject.uniqueMethodWithName("inlineable"), isPresent());
+ // The field A.INSTANCE has been accessed to allow inlining of A.inlineable().
+ assertThat(classSubject.uniqueFieldWithName("INSTANCE"), isPresent());
+ assertThat(classSubject.uniqueMethodWithName("inlineable"), not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index a9385a6..3b77ead 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
@@ -50,7 +49,8 @@
@Parameters(name = "{1}, allow access modification: {0}")
public static Collection<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
private final boolean allowAccessModification;
@@ -105,7 +105,7 @@
commandBuilder.setProguardMapOutputPath(mapFile);
}
if (parameters.isDexRuntime()) {
- commandBuilder.setMinApiLevel(AndroidApiLevel.M.getLevel());
+ commandBuilder.setMinApiLevel(parameters.getApiLevel().getLevel());
}
if (allowAccessModification) {
commandBuilder.addProguardConfiguration(
@@ -136,10 +136,19 @@
if (parameters.isDexRuntime()) {
output =
ToolHelper.runArtNoVerificationErrors(
- outputDir.resolve(DEFAULT_DEX_FILENAME).toString(), "inlining.Inlining");
+ Collections.singletonList(outputDir.resolve(DEFAULT_DEX_FILENAME).toString()),
+ "inlining.Inlining",
+ builder -> {},
+ parameters.getRuntime().asDex().getVm());
} else {
assert parameters.isCfRuntime();
- output = ToolHelper.runJavaNoVerify(outputDir, "inlining.Inlining").stdout;
+ output =
+ ToolHelper.runJava(
+ parameters.getRuntime().asCf(),
+ Collections.singletonList("-noverify"),
+ Collections.singletonList(outputDir),
+ "inlining.Inlining")
+ .stdout;
}
// Compare result with Java to make sure we have the same behavior.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/UnusedReturnValueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/UnusedReturnValueTest.java
new file mode 100644
index 0000000..2a2bb9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/UnusedReturnValueTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class UnusedReturnValueTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public UnusedReturnValueTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(UnusedReturnValueTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Builder builder = new Builder();
+ helper(builder);
+ System.out.println(builder.build());
+ }
+
+ @NeverInline
+ static Builder helper(Builder builder) {
+ builder.message = "Hello world!";
+ if (System.currentTimeMillis() > 0) {
+ return builder;
+ }
+ return null;
+ }
+ }
+
+ static class Builder {
+
+ String message;
+
+ String build() {
+ return message;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
index 2264a29..e679e80 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/IfThrowNullPointerExceptionTest.java
@@ -7,6 +7,7 @@
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.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -17,10 +18,11 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+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.MethodSubject;
-import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -41,6 +43,15 @@
}
@Test
+ public void testJVM() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(getExpectedStdout());
+ }
+
+ @Test
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForD8()
@@ -50,7 +61,7 @@
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Caught NPE", "Caught NPE");
+ .assertSuccessWithOutput(getExpectedStdout());
}
@Test
@@ -62,55 +73,104 @@
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Caught NPE", "Caught NPE");
+ .assertSuccessWithOutput(getExpectedStdout());
}
private void inspect(CodeInspector inspector) {
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
+ inspectMethod(inspector, classSubject, "testThrowNPE", false, true);
+ inspectMethod(inspector, classSubject, "testThrowNPEWithMessage", true, canUseRequireNonNull());
+ inspectMethod(inspector, classSubject, "testThrowNull", false, true);
+ }
- for (String methodName : ImmutableList.of("testThrowNPE", "testThrowNull")) {
- MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
- assertThat(methodSubject, isPresent());
+ private void inspectMethod(
+ CodeInspector inspector,
+ ClassSubject classSubject,
+ String methodName,
+ boolean isNPEWithMessage,
+ boolean shouldBeOptimized) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, isPresent());
- IRCode code = methodSubject.buildIR();
+ IRCode code = methodSubject.buildIR();
+ if (shouldBeOptimized) {
assertEquals(1, code.blocks.size());
BasicBlock entryBlock = code.entryBlock();
- assertEquals(3, entryBlock.getInstructions().size());
+ assertEquals(
+ 3 + BooleanUtils.intValue(isNPEWithMessage), entryBlock.getInstructions().size());
assertTrue(entryBlock.getInstructions().getFirst().isArgument());
assertTrue(entryBlock.getInstructions().getLast().isReturn());
- Instruction nullCheckInstruction = entryBlock.getInstructions().get(1);
- if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
+ Instruction nullCheckInstruction =
+ entryBlock.getInstructions().get(1 + BooleanUtils.intValue(isNPEWithMessage));
+ if (canUseRequireNonNull()) {
+ assertTrue(nullCheckInstruction.isInvokeStatic());
+ if (isNPEWithMessage) {
+ assertEquals(
+ inspector.getFactory().objectsMethods.requireNonNullWithMessage,
+ nullCheckInstruction.asInvokeStatic().getInvokedMethod());
+ } else {
+ assertEquals(
+ inspector.getFactory().objectsMethods.requireNonNull,
+ nullCheckInstruction.asInvokeStatic().getInvokedMethod());
+ }
+ } else {
+ assertFalse(isNPEWithMessage);
assertTrue(nullCheckInstruction.isInvokeVirtual());
assertEquals(
- "java.lang.Class java.lang.Object.getClass()",
- nullCheckInstruction.asInvokeVirtual().getInvokedMethod().toSourceString());
- } else {
- assertTrue(nullCheckInstruction.isInvokeStatic());
- assertEquals(
- "java.lang.Object java.util.Objects.requireNonNull(java.lang.Object)",
- nullCheckInstruction.asInvokeStatic().getInvokedMethod().toSourceString());
+ inspector.getFactory().objectMembers.getClass,
+ nullCheckInstruction.asInvokeVirtual().getInvokedMethod());
}
+ } else {
+ assertEquals(3, code.blocks.size());
}
}
+ private String getExpectedStdout() {
+ if (parameters.isCfRuntime() || canUseRequireNonNull() || isDalvik()) {
+ return StringUtils.lines("Caught NPE: null", "Caught NPE: x was null", "Caught NPE: null");
+ }
+ return StringUtils.lines(
+ "Caught NPE: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()'"
+ + " on a null object reference",
+ "Caught NPE: x was null",
+ "Caught NPE: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()'"
+ + " on a null object reference");
+ }
+
+ private boolean canUseRequireNonNull() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
+ }
+
+ private boolean isDalvik() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().getVersion().isDalvik();
+ }
+
static class TestClass {
public static void main(String[] args) {
testThrowNPE(new Object());
+ testThrowNPEWithMessage(new Object());
testThrowNull(new Object());
try {
testThrowNPE(null);
} catch (NullPointerException e) {
- System.out.println("Caught NPE");
+ System.out.println("Caught NPE: " + e.getMessage());
+ }
+ try {
+ testThrowNPEWithMessage(null);
+ } catch (NullPointerException e) {
+ System.out.println("Caught NPE: " + e.getMessage());
}
try {
testThrowNull(null);
} catch (NullPointerException e) {
- System.out.println("Caught NPE");
+ System.out.println("Caught NPE: " + e.getMessage());
}
}
@@ -120,6 +180,12 @@
}
}
+ static void testThrowNPEWithMessage(Object x) {
+ if (x == null) {
+ throw new NullPointerException("x was null");
+ }
+ }
+
static void testThrowNull(Object x) {
if (x == null) {
throw null;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
index d8b26ba..dce7b2d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineInvokeWithNullableReceiverTest.java
@@ -68,8 +68,7 @@
assertThat(methodSubject, isPresent());
// A `throw` instruction should have been synthesized into main().
- if (parameters.isCfRuntime()
- || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)) {
+ if (canUseRequireNonNull()) {
assertTrue(methodSubject.streamInstructions().anyMatch(InstructionSubject::isInvokeStatic));
} else {
assertTrue(
@@ -92,6 +91,11 @@
assertThat(otherClassSubject.uniqueMethodWithName("m"), not(isPresent()));
}
+ private boolean canUseRequireNonNull() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
+ }
+
static class TestClass {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
index 24ffad1..f9081c2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
@@ -221,6 +221,8 @@
.addInnerClasses(InliningAfterClassInitializationTest.class)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addKeepMainRule(mainClass)
+ .addOptionsModification(
+ options -> options.enableInliningOfInvokesWithClassInitializationSideEffects = false)
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningFromCurrentClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningFromCurrentClassTest.java
index 239b944..a7aa579 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningFromCurrentClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningFromCurrentClassTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize.inliner;
-import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNot.not;
@@ -12,14 +12,30 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
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.MethodSubject;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class InliningFromCurrentClassTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InliningFromCurrentClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test() throws Exception {
String expectedOutput =
@@ -29,17 +45,18 @@
"In A.inlineable1()",
"In B.inlineable2()",
"In C.<clinit>()",
- "In C.notInlineable()");
+ "In C.inlineableWithInitClass()");
testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
CodeInspector inspector =
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addInnerClasses(InliningFromCurrentClassTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableMergeAnnotations()
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
@@ -52,18 +69,19 @@
ClassSubject classC = inspector.clazz(C.class);
assertThat(classC, isPresent());
+ MethodSubject testMethod = classB.uniqueMethodWithName("test");
+ assertThat(testMethod, isPresent());
+
MethodSubject inlineable1Method = classA.uniqueMethodWithName("inlineable1");
assertThat(inlineable1Method, not(isPresent()));
MethodSubject inlineable2Method = classB.uniqueMethodWithName("inlineable2");
assertThat(inlineable2Method, not(isPresent()));
- MethodSubject notInlineableMethod = classC.uniqueMethodWithName("notInlineable");
- assertThat(notInlineableMethod, isPresent());
-
- MethodSubject testMethod = classB.uniqueMethodWithName("test");
- assertThat(testMethod, isPresent());
- assertThat(testMethod, invokesMethod(notInlineableMethod));
+ MethodSubject inlineableWithInitClassMethod =
+ classC.uniqueMethodWithName("inlineableWithInitClass");
+ assertThat(inlineableWithInitClassMethod, not(isPresent()));
+ assertThat(testMethod, accessesField(classC.uniqueFieldWithName("$r8$clinit")));
}
static class TestClass {
@@ -96,7 +114,7 @@
static void test() {
A.inlineable1();
B.inlineable2();
- C.notInlineable();
+ C.inlineableWithInitClass();
}
static void inlineable2() {
@@ -110,8 +128,8 @@
System.out.println("In C.<clinit>()");
}
- static void notInlineable() {
- System.out.println("In C.notInlineable()");
+ static void inlineableWithInitClass() {
+ System.out.println("In C.inlineableWithInitClass()");
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
index adc1f0b..50e52e2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagationWithClassInitializationTest.java
@@ -66,7 +66,7 @@
assertThat(bClassSubject, isPresent());
MethodSubject methodSubject = bClassSubject.uniqueMethodWithName("method");
- assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, not(isPresent()));
// TestClass.missingFieldValuePropagation() and TestClass.missingMethodValuePropagation() are
// absent.
@@ -85,11 +85,6 @@
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
.anyMatch(x -> x.getField() == clinitFieldSubject.getField().field));
- assertTrue(
- mainMethodSubject
- .streamInstructions()
- .filter(InstructionSubject::isInvokeStatic)
- .anyMatch(x -> x.getMethod() == methodSubject.getMethod().method));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java
new file mode 100644
index 0000000..2c61465
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingAliasTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.string;
+
+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.google.common.collect.Sets;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuilderWithEscapingAliasTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringBuilderWithEscapingAliasTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options ->
+ options.itemFactory.libraryMethodsReturningReceiver = Sets.newIdentityHashSet())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(StringUtils.times(StringUtils.lines("Hello world!"), 2));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ StringBuilder builder = new StringBuilder();
+ StringBuilder alias = builder.append("Hello");
+ builder.append(" world!");
+ System.out.println(builder.toString());
+ System.out.println(alias.toString());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index f9d9d21..f743bf2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -85,12 +85,12 @@
ClassSubject mainClass = codeInspector.clazz(MAIN);
MethodSubject mainMethod = mainClass.mainMethod();
assertThat(mainMethod, isPresent());
- int expectedCount = isR8 ? 3 : (isRelease ? 5 : 7);
+ int expectedCount = isR8 ? 4 : (isRelease ? 5 : 7);
assertEquals(expectedCount, countCall(mainMethod, "String", "valueOf"));
// Due to the different behavior regarding constant canonicalization.
- expectedCount = isR8 ? (parameters.isCfRuntime() ? 2 : 1) : 1;
+ expectedCount = isR8 ? (parameters.isCfRuntime() ? 3 : 1) : 1;
assertEquals(expectedCount, countConstNullNumber(mainMethod));
- expectedCount = isR8 ? (parameters.isCfRuntime() ? 2 : 1) : (isRelease ? 1 : 0);
+ expectedCount = isR8 ? 1 : (isRelease ? 1 : 0);
assertEquals(expectedCount, countNullStringNumber(mainMethod));
MethodSubject hideNPE = mainClass.uniqueMethodWithName("hideNPE");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index 7b9380f..e86770c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -72,6 +72,25 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path baseLibJar = baseLibJarMap.get(targetVersion);
+ Path extLibJar = extLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(baseLibJar, extLibJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, extLibJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInClasspathType_merged() throws Exception {
Path baseLibJar = baseLibJarMap.get(targetVersion);
Path libJar =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index f77cdb9..7514ff5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -6,18 +6,19 @@
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.assertNotEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
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.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -33,6 +34,9 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInCompanionTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED =
+ StringUtils.lines(
+ "B.Companion::foo", "B.Companion::foo", "B.Companion::foo", "B.Companion::foo");
private final TestParameters parameters;
@@ -63,6 +67,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = companionLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInCompanion_kept() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
@@ -70,21 +92,27 @@
// Keep everything
.addKeepRules("-keep class **.companion_lib.** { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ // To keep @JvmField annotation
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS)
+ // To keep ...$Companion structure
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(codeInspector -> inspect(codeInspector, true))
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: elt2"));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
@@ -99,31 +127,39 @@
.addKeepRules("-keep class **.I { <methods>; }")
// Keep getters for B$Companion.(eltN|foo) which will be referenced at the app.
.addKeepRules("-keepclassmembers class **.B$* { *** get*(...); }")
+ // Keep the companion instance in the B class
+ .addKeepRules("-keepclassmembers class **.B { *** Companion; }")
+ // Keep the name of companion class
+ .addKeepRules("-keepnames class **.*$Companion")
// No rule for Super, but will be kept and renamed.
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ // To keep @JvmField annotation
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS)
+ // To keep ...$Companion structure
+ .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+ .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.compile()
.inspect(codeInspector -> inspect(codeInspector, false))
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
- .compileRaw();
+ .compile();
- // TODO(b/70169921): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: elt1"));
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: elt2"));
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: foo"));
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".companion_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspect(CodeInspector inspector, boolean keptAll) {
final String superClassName = PKG + ".companion_lib.Super";
final String bClassName = PKG + ".companion_lib.B";
- final String companionClassName = PKG + ".companion_lib.B$Companion";
+ final String companionClassName = bClassName + "$Companion";
ClassSubject sup = inspector.clazz(superClassName);
if (keptAll) {
@@ -147,6 +183,16 @@
supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
}
+ // The backing field for the property in the companion, with @JvmField
+ FieldSubject elt2 = impl.uniqueFieldWithName("elt2");
+ assertThat(elt2, isPresent());
+ assertThat(elt2, not(isRenamed()));
+
+ FieldSubject companionObject = impl.uniqueFieldWithName("Companion");
+ assertThat(companionObject, isPresent());
+ assertThat(companionObject, not(isRenamed()));
+ assertEquals(companionObject.getFinalName(), kmClass.getCompanionObject());
+
// Bridge for the property in the companion that needs a backing field.
MethodSubject elt1Bridge = impl.uniqueMethodWithName("access$getElt1$cp");
if (keptAll) {
@@ -155,6 +201,7 @@
} else {
assertThat(elt1Bridge, isRenamed());
}
+
// With @JvmField, no bridge is added.
MethodSubject elt2Bridge = impl.uniqueMethodWithName("access$getElt2$cp");
assertThat(elt2Bridge, not(isPresent()));
@@ -164,23 +211,20 @@
assertThat(fooBridge, not(isPresent()));
ClassSubject companion = inspector.clazz(companionClassName);
- if (keptAll) {
- assertThat(companion, isPresent());
- assertThat(companion, not(isRenamed()));
- } else {
- assertThat(companion, isRenamed());
- }
+ assertThat(companion, isPresent());
+ assertThat(companion, not(isRenamed()));
- // TODO(b/70169921): Assert impl's KmClass points to the correct companion object and class.
+ List<String> nestedClassDescriptors = kmClass.getNestedClassDescriptors();
+ assertEquals(1, nestedClassDescriptors.size());
+ assertEquals(companion.getFinalDescriptor(), nestedClassDescriptors.get(0));
kmClass = companion.getKmClass();
assertThat(kmClass, isPresent());
KmPropertySubject kmProperty = kmClass.kmPropertyWithUniqueName("elt1");
assertThat(kmProperty, isPresent());
- // TODO(b/70169921): property in companion with @JvmField is missing.
kmProperty = kmClass.kmPropertyWithUniqueName("elt2");
- assertThat(kmProperty, not(isPresent()));
+ assertThat(kmProperty, isPresent());
kmProperty = kmClass.kmPropertyWithUniqueName("foo");
assertThat(kmProperty, isPresent());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 85a1869..a9f1655 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -68,6 +68,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = extLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInExtensionFunction_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index 06fcd3a..afd2ac6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -67,6 +67,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = extLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInExtensionProperty_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index e72c314..fad1701 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -66,6 +66,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = funLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".function_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInFunction_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
@@ -170,7 +188,7 @@
KmClassSubject kmClass = sup.getKmClass();
assertThat(kmClass, isPresent());
- // TODO(b/70169921): would be better to look up function with the original name, "foo".
+ // TODO(b/151194869): would be better to look up function with the original name, "foo".
KmFunctionSubject kmFunction = kmClass.kmFunctionWithUniqueName(foo.getFinalName());
assertThat(kmFunction, isPresent());
assertThat(kmFunction, not(isExtensionFunction()));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index 0e41755..79ad6e5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -69,6 +69,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = defaultValueLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInFunctionWithDefaultValue() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 68714f6..fea7b3e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -69,6 +69,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = varargLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInFunctionWithVararg() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index 0bca5ee..71dd5df 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -79,6 +79,15 @@
}
@Test
+ public void smokeTest() throws Exception {
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion))
+ .addClasspath(extLibJarMap.get(targetVersion), appJarMap.get(targetVersion))
+ .run(parameters.getRuntime(), PKG + ".libtype_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testR8() throws Exception {
String main = PKG + ".libtype_app.MainKt";
Path out =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
index 5b9dfed..477c5e6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -16,10 +16,12 @@
import static org.junit.Assert.assertTrue;
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.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -39,6 +41,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInMultifileClassTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED = StringUtils.lines(", 1, 2, 3");
private final TestParameters parameters;
@@ -71,6 +74,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = multifileLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".multifileclass_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInMultifileClass_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
@@ -88,9 +109,9 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
+ // TODO(b/151193860): update to just .compile() once fixed.
.compileRaw();
- // TODO(b/70169921): should be able to compile!
+ // TODO(b/151193860): should be able to compile!
assertNotEquals(0, kotlinTestCompileResult.exitCode);
assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
}
@@ -133,9 +154,9 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
+ // TODO(b/151193860): update to just .compile() once fixed.
.compileRaw();
- // TODO(b/70169921): should be able to compile!
+ // TODO(b/151193860): should be able to compile!
assertNotEquals(0, kotlinTestCompileResult.exitCode);
assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
}
@@ -193,7 +214,7 @@
kmPackage.kmFunctionExtensionWithUniqueName("commaSeparatedJoinOfInt");
assertThat(kmFunction, isPresent());
assertThat(kmFunction, isExtensionFunction());
- // TODO(b/70169921): Inspect that parameter type has a correct type argument, Int.
- // TODO(b/70169921): Inspect that the name in KmFunction is still 'join' so that apps can refer.
+ // TODO(b/151193860): Inspect parameter type has a correct type argument, Int.
+ // TODO(b/151193860): Inspect the name in KmFunction is still 'join' so that apps can refer.
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 0e734ea..f4530b3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -63,6 +63,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = nestedLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/nested_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".nested_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInNestedClass() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index 05acc76..54cd15c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -61,6 +61,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = parameterTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInParameterType_renamed() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 34abe12..e58725d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -10,7 +10,6 @@
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 static org.junit.Assert.assertNull;
@@ -18,6 +17,7 @@
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.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -35,6 +35,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInPropertyTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED_GETTER = StringUtils.lines("true", "false", "Hey Jude");
private final TestParameters parameters;
@@ -65,6 +66,25 @@
}
@Test
+ public void smokeTest_getterApp() throws Exception {
+ Path libJar = propertyTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
+ .assertSuccessWithOutput(EXPECTED_GETTER);
+ }
+
+ @Test
public void testMetadataInProperty_getterOnly() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
@@ -89,7 +109,7 @@
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt")
- .assertSuccessWithOutputLines("true", "false", "Hey Jude");
+ .assertSuccessWithOutput(EXPECTED_GETTER);
}
private void inspectGetterOnly(CodeInspector inspector) {
@@ -139,6 +159,25 @@
}
@Test
+ public void smokeTest_setterApp() throws Exception {
+ Path libJar = propertyTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt")
+ .assertSuccessWithOutputLines();
+ }
+
+ @Test
public void testMetadataInProperty_setterOnly() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 4fc0029..ceced0d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -61,6 +61,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = propertyTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInProperty_renamed() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index 21fcbb6..09d4a5a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -61,6 +61,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = returnTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInReturnType_renamed() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index 373dc40..b09ef7e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -69,6 +69,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = sealedLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "valid"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInSealedClass_valid() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index 9de95b4..3c02dac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -63,6 +63,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = superTypeLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInSupertype_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index 8b9f276..094362c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -12,9 +12,11 @@
import static org.junit.Assert.assertNotEquals;
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.shaking.ProguardKeepAttributes;
+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.KmPackageSubject;
@@ -30,6 +32,9 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInTypeAliasTest extends KotlinMetadataTestBase {
+ private static final String EXPECTED =
+ StringUtils.lines("Impl::foo", "Program::foo", "true", "42");
+
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0} target: {1}")
@@ -59,6 +64,24 @@
}
@Test
+ public void smokeTest() throws Exception {
+ Path libJar = typeAliasLibJarMap.get(targetVersion);
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
public void testMetadataInTypeAlias_renamed() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
@@ -79,9 +102,9 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/70169921): update to just .compile() once fixed.
+ // TODO(b/151194785): update to just .compile() once fixed.
.compileRaw();
- // TODO(b/70169921): should be able to compile!
+ // TODO(b/151194785): should be able to compile!
assertNotEquals(0, kotlinTestCompileResult.exitCode);
assertThat(
kotlinTestCompileResult.stderr,
@@ -107,6 +130,6 @@
// API entry is kept, hence the presence of Metadata.
KmPackageSubject kmPackage = libKt.getKmPackage();
assertThat(kmPackage, isPresent());
- // TODO(b/70169921): need further inspection: many kinds of type appearances in typealias.
+ // TODO(b/151194785): need further inspection: many kinds of type appearances in typealias.
}
}
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 31cd07d..4d66342 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
@@ -52,7 +52,7 @@
.addKeepMainRule(mainClassName)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addKeepRules("-keep class kotlin.Metadata")
- // TODO(b/70169921): if this option is settled down, this test is meaningless.
+ // TODO(b/151194540): if this option is settled down, this test is meaningless.
.addOptionsModification(o -> o.enableKotlinMetadataRewritingForRenamedClasses = false)
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
index e3da633..8e69219 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/companion_lib/lib.kt
@@ -23,6 +23,6 @@
@JvmField
val elt2: Super = B()
val foo: String
- get() = "B.Companion:foo"
+ get() = "B.Companion::foo"
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 03ab057..2b5270d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.CatchHandlers;
@@ -866,6 +867,7 @@
options,
null,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null);
ExecutorService executor = ThreadUtils.getExecutorService(options);
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
new file mode 100644
index 0000000..4de8e4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.testclasses.ClassToBeMinified;
+import com.android.tools.r8.naming.testclasses.Main;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
+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 RenameSourceFileRetraceTest extends TestBase {
+
+ private static final String FILENAME_RENAME = "FOO";
+ private static final String FILENAME_MAIN = "Main.java";
+ private static final String FILENAME_CLASS_TO_BE_MINIFIED = "ClassToBeMinified.java";
+
+ private final TestParameters parameters;
+ private final boolean isCompat;
+ private final boolean keepSourceFile;
+
+ @Parameters(name = "{0}, is compat: {1}, keep source file attribute: {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
+ }
+
+ public RenameSourceFileRetraceTest(
+ TestParameters parameters, Boolean isCompat, Boolean keepSourceFile) {
+ this.parameters = parameters;
+ this.isCompat = isCompat;
+ this.keepSourceFile = keepSourceFile;
+ }
+
+ @Test
+ public void testR8()
+ throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
+ R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+ isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend());
+ if (keepSourceFile) {
+ r8TestBuilder.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
+ }
+ String minifiedFileName =
+ (keepSourceFile && isCompat) ? FILENAME_CLASS_TO_BE_MINIFIED : getDefaultExpectedName();
+ String mainFileName = keepSourceFile ? FILENAME_MAIN : getDefaultExpectedName();
+ r8TestBuilder
+ .addProgramClasses(ClassToBeMinified.class, Main.class)
+ .addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString("ClassToBeMinified.foo()"))
+ .inspectFailure(inspector -> inspectOutput(inspector, minifiedFileName, mainFileName))
+ .inspectStackTrace(stackTrace -> inspectStackTrace(stackTrace, FILENAME_MAIN));
+ }
+
+ @Test
+ public void testRenameSourceFileR8()
+ throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
+ R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+ isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend());
+ if (keepSourceFile) {
+ r8TestBuilder.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
+ }
+ String expectedName = getDefaultExpectedName(FILENAME_RENAME);
+ r8TestBuilder
+ .addProgramClasses(ClassToBeMinified.class, Main.class)
+ .addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE)
+ .addKeepRules("-renamesourcefileattribute " + FILENAME_RENAME)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString("ClassToBeMinified.foo()"))
+ .inspectFailure(inspector -> inspectOutput(inspector, expectedName, expectedName))
+ .inspectStackTrace(stackTrace -> inspectStackTrace(stackTrace, expectedName));
+ }
+
+ private String getDefaultExpectedName() {
+ return getDefaultExpectedName(parameters.getBackend() == Backend.CF ? "SourceFile" : "");
+ }
+
+ private String getDefaultExpectedName(String name) {
+ if (!isCompat && !keepSourceFile) {
+ return null;
+ } else {
+ return name;
+ }
+ }
+
+ private void inspectOutput(
+ CodeInspector inspector, String classToBeMinifiedFilename, String mainClassFilename) {
+ inspectSourceFileForClass(inspector, Main.class, mainClassFilename);
+ inspectSourceFileForClass(inspector, ClassToBeMinified.class, classToBeMinifiedFilename);
+ }
+
+ private void inspectSourceFileForClass(CodeInspector inspector, Class<?> clazz, String expected) {
+ ClassSubject classToBeMinifiedSubject = inspector.clazz(clazz);
+ assertThat(classToBeMinifiedSubject, isPresent());
+ DexClass dexClass = classToBeMinifiedSubject.getDexClass();
+ String actualString = dexClass.sourceFile == null ? null : dexClass.sourceFile.toString();
+ assertEquals(expected, actualString);
+ }
+
+ private void inspectStackTrace(StackTrace stackTrace, String mainFileName)
+ throws NoSuchMethodException {
+ if (!keepSourceFile) {
+ return;
+ }
+ assertEquals(2, stackTrace.getStackTraceLines().size());
+ MethodReference classToBeMinifiedFoo =
+ Reference.methodFromMethod(ClassToBeMinified.class.getDeclaredMethod("foo"));
+ MethodReference mainMain =
+ Reference.methodFromMethod(Main.class.getDeclaredMethod("main", String[].class));
+ LinePosition expectedStack =
+ LinePosition.stack(
+ LinePosition.create(classToBeMinifiedFoo, 1, 13, FILENAME_CLASS_TO_BE_MINIFIED),
+ LinePosition.create(mainMain, 1, 10, mainFileName));
+ assertThat(stackTrace, containsLinePositions(expectedStack));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
index 9b2c7c4..a55e1d5 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
@@ -9,10 +9,12 @@
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -23,14 +25,16 @@
@RunWith(Parameterized.class)
public class DesugarLambdaRetraceTest extends RetraceTestBase {
- @Parameters(name = "{0}, mode: {1}")
+ @Parameters(name = "{0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ CompilationMode.values(),
+ BooleanUtils.values());
}
- public DesugarLambdaRetraceTest(TestParameters parameters, CompilationMode mode) {
- super(parameters, mode);
+ public DesugarLambdaRetraceTest(TestParameters parameters, CompilationMode mode, boolean compat) {
+ super(parameters, mode, compat);
}
@Override
@@ -122,12 +126,14 @@
@Test
public void testLineNumberTableOnly() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of("-keepattributes LineNumberTable"), this::checkIsSameExceptForFileName);
}
@Test
public void testNoLineNumberTable() throws Exception {
+ assumeTrue(compat);
runTest(ImmutableList.of(), this::checkIsSameExceptForFileNameAndLineNumber);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
index 1427865..f049056 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -20,14 +21,17 @@
@RunWith(Parameterized.class)
public class DesugarStaticInterfaceMethodsRetraceTest extends RetraceTestBase {
- @Parameters(name = "{0}, mode: {1}")
+ @Parameters(name = "{0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ CompilationMode.values(),
+ BooleanUtils.values());
}
- public DesugarStaticInterfaceMethodsRetraceTest(TestParameters parameters, CompilationMode mode) {
- super(parameters, mode);
+ public DesugarStaticInterfaceMethodsRetraceTest(
+ TestParameters parameters, CompilationMode mode, boolean compat) {
+ super(parameters, mode, compat);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
index 4ffc7de..48fc320 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
@@ -9,10 +9,12 @@
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -23,14 +25,16 @@
@RunWith(Parameterized.class)
public class InliningRetraceTest extends RetraceTestBase {
- @Parameters(name = "{0}, mode: {1}")
+ @Parameters(name = "{0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ CompilationMode.values(),
+ BooleanUtils.values());
}
- public InliningRetraceTest(TestParameters parameters, CompilationMode mode) {
- super(parameters, mode);
+ public InliningRetraceTest(TestParameters parameters, CompilationMode mode, boolean compat) {
+ super(parameters, mode, compat);
}
@Override
@@ -54,6 +58,7 @@
@Test
public void testLineNumberTableOnly() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of("-keepattributes LineNumberTable"),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
@@ -64,6 +69,7 @@
@Test
public void testNoLineNumberTable() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of(),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
index 3da28ae..adc5cb8 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.naming.retrace;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -18,15 +18,17 @@
public abstract class RetraceTestBase extends TestBase {
protected TestParameters parameters;
protected CompilationMode mode;
+ protected boolean compat;
- public RetraceTestBase(TestParameters parameters, CompilationMode mode) {
+ public RetraceTestBase(TestParameters parameters, CompilationMode mode, boolean compat) {
this.parameters = parameters;
this.mode = mode;
+ this.compat = compat;
}
public StackTrace expectedStackTrace;
- public void configure(R8FullTestBuilder builder) {}
+ public void configure(R8TestBuilder builder) {}
public Collection<Class<?>> getClasses() {
return ImmutableList.of(getMainClass());
@@ -49,7 +51,7 @@
throws Exception {
R8TestRunResult result =
- testForR8(parameters.getBackend())
+ (compat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
.setMode(mode)
.enableProguardTestOptions()
.addProgramClasses(getClasses())
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index d4b758e..2d5e31b 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -205,6 +205,10 @@
return originalStderr;
}
+ public List<StackTraceLine> getStackTraceLines() {
+ return stackTraceLines;
+ }
+
public static StackTrace extractFromArt(String stderr, DexVm vm) {
List<StackTraceLine> stackTraceLines = new ArrayList<>();
List<String> stderrLines = StringUtils.splitLines(stderr);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index 6cd268f..46da60b 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -7,12 +7,14 @@
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.HashSet;
@@ -26,18 +28,21 @@
public class VerticalClassMergingRetraceTest extends RetraceTestBase {
private Set<StackTraceLine> haveSeenLines = new HashSet<>();
- @Parameters(name = "{0}, mode: {1}")
+ @Parameters(name = "{0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), CompilationMode.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ CompilationMode.values(),
+ BooleanUtils.values());
}
- public VerticalClassMergingRetraceTest(TestParameters parameters, CompilationMode mode) {
- super(parameters, mode);
+ public VerticalClassMergingRetraceTest(
+ TestParameters parameters, CompilationMode mode, boolean compat) {
+ super(parameters, mode, compat);
}
@Override
- public void configure(R8FullTestBuilder builder) {
+ public void configure(R8TestBuilder builder) {
builder.enableInliningAnnotations();
}
@@ -87,6 +92,7 @@
@Test
public void testLineNumberTableOnly() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of("-keepattributes LineNumberTable"),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
@@ -125,6 +131,7 @@
// at com.android.tools.r8.naming.retraceproguard.ResourceWrapper.foo(ResourceWrapper.java:0)
// at com.android.tools.r8.naming.retraceproguard.MainApp.main(MainApp.java:7)
// since the synthetic bridge belongs to ResourceWrapper.foo.
+ assumeTrue(compat);
haveSeenLines.clear();
runTest(
ImmutableList.of(),
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
index 42f2857..a527d93 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Ignore;
@@ -23,13 +24,14 @@
@RunWith(Parameterized.class)
public class DesugarLambdaRetraceTest extends RetraceTestBase {
- @Parameters(name = "Backend: {0}, mode: {1}")
+ @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
- return buildParameters(ToolHelper.getBackends(), CompilationMode.values());
+ return buildParameters(
+ ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values());
}
- public DesugarLambdaRetraceTest(Backend backend, CompilationMode mode) {
- super(backend, mode);
+ public DesugarLambdaRetraceTest(Backend backend, CompilationMode mode, boolean compat) {
+ super(backend, mode, compat);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
index acd5baa..2034f5d 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import org.junit.Test;
@@ -20,13 +21,15 @@
@RunWith(Parameterized.class)
public class DesugarStaticInterfaceMethodsRetraceTest extends RetraceTestBase {
- @Parameters(name = "Backend: {0}, mode: {1}")
+ @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
- return buildParameters(ToolHelper.getBackends(), CompilationMode.values());
+ return buildParameters(
+ ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values());
}
- public DesugarStaticInterfaceMethodsRetraceTest(Backend backend, CompilationMode mode) {
- super(backend, mode);
+ public DesugarStaticInterfaceMethodsRetraceTest(
+ Backend backend, CompilationMode mode, boolean compat) {
+ super(backend, mode, compat);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
index 4f84c8f..aafa229 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
@@ -8,11 +8,13 @@
import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.Collections;
@@ -28,11 +30,12 @@
public static Collection<Object[]> data() {
return ToolHelper.getDexVm().getVersion() == Version.V5_1_1
? Collections.emptyList()
- : buildParameters(ToolHelper.getBackends(), CompilationMode.values());
+ : buildParameters(
+ ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values());
}
- public InliningRetraceTest(Backend backend, CompilationMode mode) {
- super(backend, mode);
+ public InliningRetraceTest(Backend backend, CompilationMode mode, boolean value) {
+ super(backend, mode, value);
}
@Override
@@ -57,6 +60,7 @@
@Test
public void testLineNumberTableOnly() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of("-keepattributes LineNumberTable"),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
@@ -67,6 +71,7 @@
@Test
public void testNoLineNumberTable() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of(),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
index 4a877e2..e94483f 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.naming.retraceproguard;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.google.common.collect.ImmutableList;
@@ -17,15 +17,17 @@
public abstract class RetraceTestBase extends TestBase {
protected Backend backend;
protected CompilationMode mode;
+ protected boolean compat;
- public RetraceTestBase(Backend backend, CompilationMode mode) {
+ public RetraceTestBase(Backend backend, CompilationMode mode, boolean compat) {
this.backend = backend;
this.mode = mode;
+ this.compat = compat;
}
public StackTrace expectedStackTrace;
- public void configure(R8FullTestBuilder builder) {}
+ public void configure(R8TestBuilder builder) {}
public Collection<Class<?>> getClasses() {
return ImmutableList.of(getMainClass());
@@ -48,7 +50,7 @@
throws Exception {
R8TestRunResult result =
- testForR8(backend)
+ (compat ? testForR8Compat(backend) : testForR8(backend))
.setMode(mode)
.enableProguardTestOptions()
.addProgramClasses(getClasses())
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
index 42264a2..2874bf9 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -7,12 +7,14 @@
import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.HashSet;
@@ -26,17 +28,18 @@
public class VerticalClassMergingRetraceTest extends RetraceTestBase {
private Set<StackTraceLine> haveSeenLines = new HashSet<>();
- @Parameters(name = "Backend: {0}, mode: {1}")
+ @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
- return buildParameters(ToolHelper.getBackends(), CompilationMode.values());
+ return buildParameters(
+ ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values());
}
- public VerticalClassMergingRetraceTest(Backend backend, CompilationMode mode) {
- super(backend, mode);
+ public VerticalClassMergingRetraceTest(Backend backend, CompilationMode mode, boolean compat) {
+ super(backend, mode, compat);
}
@Override
- public void configure(R8FullTestBuilder builder) {
+ public void configure(R8TestBuilder builder) {
builder.enableInliningAnnotations();
}
@@ -83,6 +86,7 @@
@Test
public void testLineNumberTableOnly() throws Exception {
+ assumeTrue(compat);
runTest(
ImmutableList.of("-keepattributes LineNumberTable"),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
@@ -97,6 +101,7 @@
@Test
public void testNoLineNumberTable() throws Exception {
+ assumeTrue(compat);
haveSeenLines.clear();
runTest(
ImmutableList.of(),
diff --git a/src/test/java/com/android/tools/r8/naming/testclasses/ClassToBeMinified.java b/src/test/java/com/android/tools/r8/naming/testclasses/ClassToBeMinified.java
new file mode 100644
index 0000000..da6f9c0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/testclasses/ClassToBeMinified.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.testclasses;
+
+import com.android.tools.r8.NeverInline;
+
+public class ClassToBeMinified {
+
+ @NeverInline
+ public static void foo() {
+ throw new RuntimeException("ClassToBeMinified.foo()");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/testclasses/Main.java b/src/test/java/com/android/tools/r8/naming/testclasses/Main.java
new file mode 100644
index 0000000..1e37d4c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/testclasses/Main.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.testclasses;
+
+public class Main {
+
+ public static void main(String[] args) {
+ ClassToBeMinified.foo();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
new file mode 100644
index 0000000..deec1e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.singletarget;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+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.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Set;
+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 InstantiatedLowerBoundTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public InstantiatedLowerBoundTest(TestParameters parameters) {
+ // Empty to satisfy construction of none-runtime.
+ }
+
+ @Test
+ public void testSingleTargetLowerBoundInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeB = buildType(B.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ClassTypeLatticeElement latticeB =
+ ClassTypeLatticeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+ assertNotNull(singleTarget);
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ assertEquals(fooB, singleTarget.method);
+ }
+
+ @Test
+ public void testSingleTargetLowerBoundInMiddleInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, C.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeB = buildType(B.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ ClassTypeLatticeElement latticeB =
+ ClassTypeLatticeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
+ assertNotNull(singleTarget);
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ assertEquals(fooB, singleTarget.method);
+ }
+
+ @Test
+ public void testSingleTargetLowerAllInstantiated() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(A.class, B.class, C.class, MainAllInstantiated.class).build(),
+ factory ->
+ new ArrayList<>(
+ buildKeepRuleForClassAndMethods(MainAllInstantiated.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexType typeC = buildType(C.class, appInfo.dexItemFactory());
+ DexType typeMain = buildType(MainAllInstantiated.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory());
+ ResolutionResult resolution = appInfo.resolveMethod(typeA, fooA);
+ DexProgramClass context = appView.definitionForProgramType(typeMain);
+ DexProgramClass upperBound = appView.definitionForProgramType(typeA);
+ DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
+ LookupResult lookupResult =
+ resolution.lookupVirtualDispatchTargets(context, appInfo, upperBound, lowerBound);
+ Set<DexMethod> expected = Sets.newIdentityHashSet();
+ expected.add(fooA);
+ expected.add(fooB);
+ expected.add(fooC);
+ assertTrue(lookupResult.isLookupResultSuccess());
+ Set<DexMethod> actual = Sets.newIdentityHashSet();
+ lookupResult
+ .asLookupResultSuccess()
+ .forEach(
+ clazzAndMethod -> actual.add(clazzAndMethod.getMethod().method),
+ lambdaTarget -> {
+ assert false;
+ });
+ assertEquals(expected, actual);
+ ClassTypeLatticeElement latticeC =
+ ClassTypeLatticeElement.create(typeC, Nullability.definitelyNotNull(), appView);
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeC);
+ assertNull(singleTarget);
+ }
+
+ public static class A {
+
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class B extends A {
+
+ @Override
+ public void foo() {
+ System.out.println("B.foo");
+ }
+ }
+
+ public static class C extends B {
+
+ @Override
+ public void foo() {
+ System.out.println("C.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new B();
+ }
+ }
+
+ public static class MainAllInstantiated {
+
+ public static void main(String[] args) {
+ new A();
+ new B();
+ new C();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
new file mode 100644
index 0000000..870c9cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.resolution.singletarget;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
+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 SuccessAndInvalidLookupTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public SuccessAndInvalidLookupTest(TestParameters parameters) {
+ // Empty to satisfy construction of none-runtime.
+ }
+
+ @Test
+ public void testSingleTargetWithInvalidInvokeInterfaceInvoke() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(I.class, A.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, null);
+ assertNotNull(singleTarget);
+ assertEquals(fooA, singleTarget.method);
+ DexEncodedMethod invalidSingleTarget =
+ appInfo.lookupSingleVirtualTarget(fooA, typeMain, true, t -> false, typeA, null);
+ assertNull(invalidSingleTarget);
+ }
+
+ @Test
+ public void testSingleTargetWithInvalidInvokeVirtualInvoke() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildClasses(I.class, A.class, Main.class).build(),
+ factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
+ DexType typeA = buildType(I.class, appInfo.dexItemFactory());
+ DexMethod fooI = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
+ DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
+ DexEncodedMethod singleTarget =
+ appInfo.lookupSingleVirtualTarget(fooI, typeMain, true, t -> false, typeA, null);
+ assertNotNull(singleTarget);
+ assertEquals(fooA, singleTarget.method);
+ DexEncodedMethod invalidSingleTarget =
+ appInfo.lookupSingleVirtualTarget(fooI, typeMain, false, t -> false, typeA, null);
+ assertNull(invalidSingleTarget);
+ }
+
+ public interface I {
+
+ void foo();
+ }
+
+ public static class A implements I {
+
+ @Override
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 809a521..3cabc0e 100644
--- a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -110,6 +111,7 @@
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineMethod.class)
.enableInliningAnnotations()
+ .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::checkSomething)
@@ -125,8 +127,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessInstanceMethod")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessInstanceMethod")
.addWithoutFileNameAndLineNumber(TestClassForInlineMethod.class, "main")
.build())));
@@ -138,6 +138,7 @@
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineField.class)
.enableInliningAnnotations()
+ .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::checkSomething)
@@ -153,8 +154,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessInstanceField")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessInstanceField")
.addWithoutFileNameAndLineNumber(TestClassForInlineField.class, "main")
.build())));
@@ -167,6 +166,7 @@
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineStaticField.class)
.enableInliningAnnotations()
+ .addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::checkSomething)
@@ -183,8 +183,6 @@
isSameExceptForFileNameAndLineNumber(
createStackTraceBuilder()
.addWithoutFileNameAndLineNumber(
- Result.class, "methodWhichAccessStaticField")
- .addWithoutFileNameAndLineNumber(
A.class, "inlineMethodWhichAccessStaticField")
.addWithoutFileNameAndLineNumber(
TestClassForInlineStaticField.class, "main")
@@ -205,8 +203,8 @@
}
private boolean canUseRequireNonNull() {
- return parameters.isCfRuntime()
- || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
}
static class TestClassForInlineMethod {
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index d70f22b..1fa811a 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.utils.codeinspector.Matchers.containsInlinePosition;
+import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -26,7 +26,7 @@
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.Matchers.InlinePosition;
+import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Path;
@@ -40,6 +40,7 @@
@RunWith(Parameterized.class)
public class KotlinInlineFunctionInSameFileRetraceTests extends TestBase {
+ private static final String FILENAME_INLINE = "InlineFunctionsInSameFile.kt";
private static final String MAIN = "retrace.InlineFunctionsInSameFileKt";
private final TestParameters parameters;
@@ -97,16 +98,18 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(MAIN).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
kotlinInspector
.clazz("retrace.InlineFunctionsInSameFileKt")
.uniqueMethodWithName("foo")
.asFoundMethodSubject(),
1,
- 8),
- InlinePosition.create(mainSubject.asFoundMethodSubject(), 1, 43));
+ 8,
+ FILENAME_INLINE),
+ LinePosition.create(
+ mainSubject.asFoundMethodSubject(), 1, 43, FILENAME_INLINE));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -115,7 +118,7 @@
StackTrace stackTrace,
CodeInspector codeInspector,
MethodSubject mainSubject,
- InlinePosition inlineStack) {
+ LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
RetraceMethodResult retraceResult =
mainSubject
@@ -125,6 +128,6 @@
.retraceLinePosition(codeInspector.retrace());
assertThat(retraceResult, isInlineFrame());
assertThat(retraceResult, isInlineStack(inlineStack));
- assertThat(stackTrace, containsInlinePosition(inlineStack));
+ assertThat(stackTrace, containsLinePositions(inlineStack));
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index b4d302b..05b028a 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.utils.codeinspector.Matchers.containsInlinePosition;
+import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -28,7 +28,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.Matchers.InlinePosition;
+import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Path;
@@ -43,6 +43,9 @@
public class KotlinInlineFunctionRetraceTest extends TestBase {
private final TestParameters parameters;
+ // TODO(b/151132660): Fix filename
+ private static final String FILENAME_INLINE_STATIC = "InlineFunctionKt.kt";
+ private static final String FILENAME_INLINE_INSTANCE = "InlineFunction.kt";
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -93,6 +96,7 @@
public void testRetraceKotlinInlineStaticFunction()
throws ExecutionException, CompilationFailedException, IOException {
String main = "retrace.MainKt";
+ String mainFileName = "Main.kt";
Path kotlinSources = compilationResults.apply(parameters.getRuntime());
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
@@ -109,10 +113,11 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(main).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(inlineExceptionStatic(kotlinInspector), 2, 8),
- InlinePosition.create(mainSubject.asFoundMethodSubject(), 2, 15));
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
+ inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 15, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -121,6 +126,7 @@
public void testRetraceKotlinInlineInstanceFunction()
throws ExecutionException, CompilationFailedException, IOException {
String main = "retrace.MainInstanceKt";
+ String mainFileName = "MainInstance.kt";
Path kotlinSources = compilationResults.apply(parameters.getRuntime());
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
@@ -137,10 +143,14 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(main).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(inlineExceptionInstance(kotlinInspector), 2, 15),
- InlinePosition.create(mainSubject.asFoundMethodSubject(), 2, 13));
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
+ inlineExceptionInstance(kotlinInspector),
+ 2,
+ 15,
+ FILENAME_INLINE_INSTANCE),
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 13, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -149,6 +159,7 @@
public void testRetraceKotlinNestedInlineFunction()
throws ExecutionException, CompilationFailedException, IOException {
String main = "retrace.MainNestedKt";
+ String mainFileName = "MainNested.kt";
Path kotlinSources = compilationResults.apply(parameters.getRuntime());
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
@@ -165,12 +176,13 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(main).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(inlineExceptionStatic(kotlinInspector), 3, 8),
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
+ inlineExceptionStatic(kotlinInspector), 3, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
// retrace.NestedInlineFunctionKt.nestedInline(line 10).
- InlinePosition.create(mainSubject.asFoundMethodSubject(), 3, 19));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 3, 19, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -179,6 +191,7 @@
public void testRetraceKotlinNestedInlineFunctionOnFirstLine()
throws ExecutionException, CompilationFailedException, IOException {
String main = "retrace.MainNestedFirstLineKt";
+ String mainFileName = "MainNestedFirstLine.kt";
Path kotlinSources = compilationResults.apply(parameters.getRuntime());
CodeInspector kotlinInspector = new CodeInspector(kotlinSources);
testForR8(parameters.getBackend())
@@ -195,12 +208,13 @@
.inspectStackTrace(
(stackTrace, codeInspector) -> {
MethodSubject mainSubject = codeInspector.clazz(main).uniqueMethodWithName("main");
- InlinePosition inlineStack =
- InlinePosition.stack(
- InlinePosition.create(inlineExceptionStatic(kotlinInspector), 2, 8),
+ LinePosition inlineStack =
+ LinePosition.stack(
+ LinePosition.create(
+ inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
// retrace.NestedInlineFunctionKt.nestedInlineOnFirstLine(line 15).
- InlinePosition.create(mainSubject.asFoundMethodSubject(), 2, 20));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 20, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -209,7 +223,7 @@
StackTrace stackTrace,
CodeInspector codeInspector,
MethodSubject mainSubject,
- InlinePosition inlineStack) {
+ LinePosition inlineStack) {
assertThat(mainSubject, isPresent());
RetraceMethodResult retraceResult =
mainSubject
@@ -219,6 +233,6 @@
.retraceLinePosition(codeInspector.retrace());
assertThat(retraceResult, isInlineFrame());
assertThat(retraceResult, isInlineStack(inlineStack));
- assertThat(stackTrace, containsInlinePosition(inlineStack));
+ assertThat(stackTrace, containsLinePositions(inlineStack));
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 174dd5e..47dc5c3 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
+import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -172,6 +173,11 @@
runRetraceTest(new NamedModuleStackTrace());
}
+ @Test
+ public void testUnknownSourceStackTrace() {
+ runRetraceTest(new UnknownSourceStackTrace());
+ }
+
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
new file mode 100644
index 0000000..4219979
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownSourceStackTrace.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class UnknownSourceStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+ " at a.a.a(Unknown Source)",
+ " at a.a.a(Unknown Source)",
+ " at com.android.tools.r8.R8.main(Unknown Source)",
+ "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+ " at a.a.a(Unknown Source)",
+ " ... 42 more");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+ " at com.android.tools.r8.R8.bar(R8.java)",
+ " <OR> at com.android.tools.r8.R8.foo(R8.java)",
+ " at com.android.tools.r8.R8.bar(R8.java)",
+ " <OR> at com.android.tools.r8.R8.foo(R8.java)",
+ " at com.android.tools.r8.R8.main(R8.java)",
+ "Caused by: com.android.tools.r8.CompilationException: foo[parens](Source:3)",
+ " at com.android.tools.r8.R8.bar(R8.java)",
+ " <OR> at com.android.tools.r8.R8.foo(R8.java)",
+ " ... 42 more");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.R8 -> a.a:", " void foo(int) -> a", " void bar(int, int) -> a");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
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 78a36f2..91f0c4d 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
@@ -49,6 +49,7 @@
private void configure(InternalOptions options) {
options.enableEnumValueOptimization = enableOptimization;
options.enableEnumSwitchMapRemoval = enableOptimization;
+ options.enableEnumUnboxing = false;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
index 397c054..ac18813 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldReadsJasminTest.java
@@ -252,7 +252,31 @@
" getstatic Empty/sField I",
" return");
- ensureFieldExistsAndReadOnlyOnce(builder, main, mainMethod, empty, "sField");
+ inspect(
+ builder,
+ inspector ->
+ ensureFieldExistsAndReadOnlyOnce(
+ inspector, main.name, mainMethod.name, empty, "sField", false),
+ inspector -> {
+ ClassSubject emptyClassSubject = inspector.clazz(empty.name);
+ assertThat(emptyClassSubject, isPresent());
+ assertEquals(1, emptyClassSubject.allStaticFields().size());
+
+ FieldSubject clinitFieldSubject = emptyClassSubject.allStaticFields().get(0);
+ assertEquals("$r8$clinit", clinitFieldSubject.getOriginalName());
+
+ ClassSubject mainClassSubject = inspector.clazz(main.name);
+ assertThat(mainClassSubject, isPresent());
+ assertThat(mainClassSubject.mainMethod(), isPresent());
+ assertTrue(
+ mainClassSubject
+ .mainMethod()
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .anyMatch(
+ instruction ->
+ instruction.getField().equals(clinitFieldSubject.getField().field)));
+ });
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index 0212a16..e23b80b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -14,6 +14,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -48,7 +49,8 @@
@Parameters(name = "{1}, include WorldGreeter: {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public ServiceLoaderTest(boolean includeWorldGreeter, TestParameters parameters) {
@@ -89,7 +91,8 @@
options.enableInliningOfInvokesWithNullableReceivers = false;
})
.enableGraphInspector()
- .setMinApi(parameters.getRuntime())
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -226,6 +229,7 @@
public static class HelloGreeter implements Greeter {
+ @NeverPropagateValue
@Override
public String greeting() {
return "Hello";
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
index 8cb9074..e47222b 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
@@ -9,18 +9,14 @@
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.io.ByteStreams;
import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -40,6 +36,8 @@
}
private static class Inlinee {
+
+ @NeverPropagateValue
public static String foo() {
return "Hello from Inlinee!";
}
@@ -80,7 +78,13 @@
assertEquals(OLD_VERSION, getBaseClassVersion(inputJar));
ProcessResult runInput = run(inputJar);
assertEquals(0, runInput.exitCode);
- Path outputJar = runR8(inputJar);
+ Path outputJar =
+ testForR8(Backend.CF)
+ .addProgramFiles(inputJar)
+ .addKeepMainRule(Base.class)
+ .enableMemberValuePropagationAnnotations()
+ .compile()
+ .writeToZip();
ProcessResult runOutput = run(outputJar);
assertEquals(runInput.toString(), runOutput.toString());
assertNotEquals(
@@ -141,19 +145,4 @@
private ProcessResult run(Path jar) throws Exception {
return ToolHelper.runJava(jar, Base.class.getName());
}
-
- private Path runR8(Path inputJar) throws Exception {
- List<String> keepRule =
- Collections.singletonList(
- "-keep class " + Base.class.getName() + " { public static void main(...); }");
- Path outputJar = temp.getRoot().toPath().resolve("output.jar");
- ToolHelper.runR8(
- R8Command.builder()
- .addProgramFiles(inputJar)
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
- .addProguardConfiguration(keepRule, Origin.unknown())
- .setOutput(outputJar, OutputMode.ClassFile)
- .build());
- return outputJar;
- }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
index 3401322..5cdc80e 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking13Test.java
@@ -12,7 +12,6 @@
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.junit.Assert;
@@ -50,7 +49,7 @@
private static void shaking13EnsureFieldWritesCorrect(CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("shaking13.Shaking");
- MethodSubject testMethod = mainClass.method("void", "fieldTest", Collections.emptyList());
+ MethodSubject testMethod = mainClass.uniqueMethodWithName("fieldTest");
Assert.assertTrue(testMethod.isPresent());
Iterator<FieldAccessInstructionSubject> iterator =
testMethod.iterateInstructions(InstructionSubject::isFieldAccess);
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 8ecb463..6aad725 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -62,7 +62,6 @@
protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
try {
- options.itemFactory.resetSortedIndices();
return new ApplicationReader(input, options, Timing.empty()).read();
} catch (IOException | ExecutionException e) {
throw new RuntimeException(e);
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 02aa682..cb9ace0 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.google.common.collect.ImmutableList;
@@ -117,6 +118,7 @@
options,
null,
GraphLense.getIdentityLense(),
+ InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null);
writer.write(executor);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 7331a22..8786f30 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -40,6 +40,9 @@
public void forAllInstanceFields(Consumer<FoundFieldSubject> inspection) {}
@Override
+ public void forAllStaticFields(Consumer<FoundFieldSubject> inspection) {}
+
+ @Override
public FieldSubject field(String type, String name) {
return new AbsentFieldSubject();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
index c467da4..a009244 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
@@ -122,4 +122,9 @@
public List<ClassSubject> getSealedSubclasses() {
return null;
}
+
+ @Override
+ public String getCompanionObject() {
+ return null;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index b693ad9..c8c6142 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -118,12 +118,20 @@
public abstract void forAllInstanceFields(Consumer<FoundFieldSubject> inspection);
+ public abstract void forAllStaticFields(Consumer<FoundFieldSubject> inspection);
+
public final List<FoundFieldSubject> allInstanceFields() {
ImmutableList.Builder<FoundFieldSubject> builder = ImmutableList.builder();
forAllInstanceFields(builder::add);
return builder.build();
}
+ public final List<FoundFieldSubject> allStaticFields() {
+ ImmutableList.Builder<FoundFieldSubject> builder = ImmutableList.builder();
+ forAllStaticFields(builder::add);
+ return builder.build();
+ }
+
public abstract FieldSubject field(String type, String name);
public abstract FieldSubject uniqueFieldWithName(String name);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 5dacc5a..86a21c1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import java.util.function.Predicate;
import org.hamcrest.Description;
@@ -12,6 +13,35 @@
public class CodeMatchers {
+ public static Matcher<MethodSubject> accessesField(FieldSubject targetSubject) {
+ if (!targetSubject.isPresent()) {
+ throw new IllegalArgumentException();
+ }
+ DexField target = targetSubject.getField().field;
+ return new TypeSafeMatcher<MethodSubject>() {
+ @Override
+ protected boolean matchesSafely(MethodSubject subject) {
+ if (!subject.isPresent()) {
+ return false;
+ }
+ if (!subject.getMethod().hasCode()) {
+ return false;
+ }
+ return subject.streamInstructions().anyMatch(isFieldAccessWithTarget(target));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("accesses field `" + target.toSourceString() + "`");
+ }
+
+ @Override
+ public void describeMismatchSafely(final MethodSubject subject, Description description) {
+ description.appendText("method did not");
+ }
+ };
+ }
+
public static Matcher<MethodSubject> invokesMethod(MethodSubject targetSubject) {
if (!targetSubject.isPresent()) {
throw new IllegalArgumentException();
@@ -44,4 +74,8 @@
public static Predicate<InstructionSubject> isInvokeWithTarget(DexMethod target) {
return instruction -> instruction.isInvoke() && instruction.getMethod() == target;
}
+
+ public static Predicate<InstructionSubject> isFieldAccessWithTarget(DexField target) {
+ return instruction -> instruction.isFieldAccess() && instruction.getField() == target;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index dc65b71..fc5c38f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -122,12 +122,8 @@
@Override
public void forAllFields(Consumer<FoundFieldSubject> inspection) {
- CodeInspector.forAll(
- dexClass.staticFields(),
- (dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
- this,
- inspection);
forAllInstanceFields(inspection);
+ forAllStaticFields(inspection);
}
@Override
@@ -140,6 +136,15 @@
}
@Override
+ public void forAllStaticFields(Consumer<FoundFieldSubject> inspection) {
+ CodeInspector.forAll(
+ dexClass.staticFields(),
+ (dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
+ this,
+ inspection);
+ }
+
+ @Override
public FieldSubject field(String type, String name) {
String obfuscatedType = codeInspector.getObfuscatedTypeName(type);
MemberNaming fieldNaming = null;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index 7b57397..71b30cb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -55,7 +55,7 @@
@Override
public boolean isSynthetic() {
- // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata
+ // TODO(b/151194785): This should return `true` conditionally if we start synthesizing @Metadata
// from scratch.
return false;
}
@@ -110,4 +110,9 @@
.map(this::getClassSubjectFromDescriptor)
.collect(Collectors.toList());
}
+
+ @Override
+ public String getCompanionObject() {
+ return kmClass.getCompanionObject();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
index 5b3338b..626388f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
@@ -80,7 +80,7 @@
}
}
- // TODO(b/70169921): Search both original and renamed names.
+ // TODO(b/151194869): Search both original and renamed names.
default KmFunctionSubject kmFunctionOrExtensionWithUniqueName(String name, boolean isExtension) {
KmFunction foundFunction = null;
for (KmFunction kmFunction : getKmDeclarationContainer().getFunctions()) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
index cdf57aa..f86f718 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -29,13 +29,13 @@
@Override
public boolean isRenamed() {
- // TODO(b/70169921): need to know the corresponding DexEncodedMethod.
+ // TODO(b/151194869): need to know the corresponding DexEncodedMethod.
return false;
}
@Override
public boolean isSynthetic() {
- // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata
+ // TODO(b/151194785): This should return `true` conditionally if we start synthesizing @Metadata
// from scratch.
return false;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
index b4e796c..d2c4fa6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
@@ -46,7 +46,7 @@
@Override
public boolean isSynthetic() {
- // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata
+ // TODO(b/151194785): This should return `true` conditionally if we start synthesizing @Metadata
// from scratch.
return false;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java
index 3cfe20d..d6bab2d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPropertySubject.java
@@ -31,14 +31,14 @@
@Override
public boolean isRenamed() {
- // TODO(b/70169921): How to determine it is renamed?
+ // TODO(b/151194869): How to determine it is renamed?
// backing field renamed? If no backing field exists, then examine getter/setter?
return false;
}
@Override
public boolean isSynthetic() {
- // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata
+ // TODO(b/151194785): This should return `true` conditionally if we start synthesizing @Metadata
// from scratch.
return false;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
index 7fb1f9f..3f5be34 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
@@ -22,4 +22,6 @@
public abstract List<String> getSealedSubclassDescriptors();
public abstract List<ClassSubject> getSealedSubclasses();
+
+ public abstract String getCompanionObject();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index 5d7ec81..d671a7d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -23,7 +23,7 @@
this.kmType = kmType;
}
- // TODO(b/145824437): This is a dup of DescriptorUtils#getDescriptorFromKmType
+ // TODO(b/151195430): This is a dup of DescriptorUtils#getDescriptorFromKmType
static String getDescriptorFromKmType(KmType kmType) {
if (kmType == null) {
return null;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 3fb684f..98f6b1e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.retrace.RetraceMethodResult;
import com.android.tools.r8.retrace.RetraceMethodResult.Element;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.Visibility;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.stream.Collectors;
@@ -21,33 +22,6 @@
public class Matchers {
- private enum Visibility {
- PUBLIC,
- PROTECTED,
- PRIVATE,
- PACKAGE_PRIVATE;
-
- @Override
- public String toString() {
- switch (this) {
- case PUBLIC:
- return "public";
-
- case PROTECTED:
- return "protected";
-
- case PRIVATE:
- return "private";
-
- case PACKAGE_PRIVATE:
- return "package-private";
-
- default:
- throw new Unreachable("Unexpected visibility");
- }
- }
- }
-
private static String type(Subject subject) {
String type = "<unknown subject type>";
if (subject instanceof ClassSubject) {
@@ -428,16 +402,16 @@
};
}
- public static Matcher<RetraceMethodResult> isInlineStack(InlinePosition startPosition) {
+ public static Matcher<RetraceMethodResult> isInlineStack(LinePosition startPosition) {
return new TypeSafeMatcher<RetraceMethodResult>() {
@Override
protected boolean matchesSafely(RetraceMethodResult item) {
- Box<InlinePosition> currentPosition = new Box<>(startPosition);
+ Box<LinePosition> currentPosition = new Box<>(startPosition);
Box<Boolean> returnValue = new Box<>();
item.forEach(
element -> {
boolean sameMethod;
- InlinePosition currentInline = currentPosition.get();
+ LinePosition currentInline = currentPosition.get();
if (currentInline == null) {
returnValue.set(false);
return;
@@ -492,70 +466,100 @@
};
}
- public static Matcher<StackTrace> containsInlinePosition(InlinePosition inlinePosition) {
+ public static Matcher<StackTrace> containsLinePositions(LinePosition linePosition) {
return new TypeSafeMatcher<StackTrace>() {
@Override
protected boolean matchesSafely(StackTrace item) {
- return containsInlineStack(item, 0, inlinePosition);
+ return containsLinePosition(item, 0, linePosition);
}
@Override
public void describeTo(Description description) {
- description.appendText("cannot be found in stack trace");
+ description.appendText(linePosition + " cannot be found in stack trace");
}
- private boolean containsInlineStack(
- StackTrace stackTrace, int index, InlinePosition currentPosition) {
- if (currentPosition == null) {
+ private boolean containsLinePosition(
+ StackTrace stackTrace, int index, LinePosition linePosition) {
+ if (linePosition == null) {
return true;
}
- if (index >= stackTrace.size()) {
- return false;
+ Matcher<StackTraceLine> lineMatcher = Matchers.matchesLinePosition(linePosition);
+ for (int i = index; i < stackTrace.getStackTraceLines().size(); i++) {
+ StackTraceLine stackTraceLine = stackTrace.get(i);
+ if (lineMatcher.matches(stackTraceLine)) {
+ return containsLinePosition(stackTrace, index + 1, linePosition.caller);
+ }
}
- StackTraceLine stackTraceLine = stackTrace.get(index);
- boolean resultHere =
- stackTraceLine.className.equals(currentPosition.getClassName())
- && stackTraceLine.methodName.equals(currentPosition.getMethodName())
- && stackTraceLine.lineNumber == currentPosition.originalPosition;
- if (resultHere && containsInlineStack(stackTrace, index + 1, currentPosition.caller)) {
- return true;
- }
- // Maybe the inline position starts from the top on the next position.
- return containsInlineStack(stackTrace, index + 1, inlinePosition);
+ return false;
}
};
}
- public static class InlinePosition {
+ public static Matcher<StackTraceLine> matchesLinePosition(LinePosition linePosition) {
+ return new TypeSafeMatcher<StackTraceLine>() {
+
+ @Override
+ protected boolean matchesSafely(StackTraceLine item) {
+ return containsLinePosition(item, linePosition);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(linePosition + " cannot be found in stack trace");
+ }
+
+ private boolean containsLinePosition(
+ StackTraceLine stackTraceLine, LinePosition currentPosition) {
+ return stackTraceLine.className.equals(currentPosition.getClassName())
+ && stackTraceLine.methodName.equals(currentPosition.getMethodName())
+ && stackTraceLine.lineNumber == currentPosition.originalPosition
+ && stackTraceLine.fileName.equals(currentPosition.filename);
+ }
+ };
+ }
+
+ public static class LinePosition {
private final MethodReference methodReference;
private final int minifiedPosition;
private final int originalPosition;
+ private final String filename;
- private InlinePosition caller;
+ private LinePosition caller;
- private InlinePosition(
- MethodReference methodReference, int minifiedPosition, int originalPosition) {
+ private LinePosition(
+ MethodReference methodReference,
+ int minifiedPosition,
+ int originalPosition,
+ String filename) {
this.methodReference = methodReference;
this.minifiedPosition = minifiedPosition;
this.originalPosition = originalPosition;
+ this.filename = filename;
}
- public static InlinePosition create(
- MethodReference methodReference, int minifiedPosition, int originalPosition) {
- return new InlinePosition(methodReference, minifiedPosition, originalPosition);
+ public static LinePosition create(
+ MethodReference methodReference,
+ int minifiedPosition,
+ int originalPosition,
+ String filename) {
+ return new LinePosition(methodReference, minifiedPosition, originalPosition, filename);
}
- public static InlinePosition create(
- FoundMethodSubject methodSubject, int minifiedPosition, int originalPosition) {
- return create(methodSubject.asMethodReference(), minifiedPosition, originalPosition);
+ public static LinePosition create(
+ FoundMethodSubject methodSubject,
+ int minifiedPosition,
+ int originalPosition,
+ String filename) {
+ return create(
+ methodSubject.asMethodReference(), minifiedPosition, originalPosition, filename);
}
- public static InlinePosition stack(InlinePosition... stack) {
+ public static LinePosition stack(LinePosition... stack) {
setCaller(1, stack);
return stack[0];
}
- private static void setCaller(int index, InlinePosition... stack) {
+ private static void setCaller(int index, LinePosition... stack) {
assert index > 0;
if (index >= stack.length) {
return;
@@ -571,5 +575,10 @@
String getClassName() {
return methodReference.getHolderClass().getTypeName();
}
+
+ @Override
+ public String toString() {
+ return getClassName() + "." + getMethodName() + "(" + filename + ":" + originalPosition + ")";
+ }
}
}
diff --git a/third_party/remapper.tar.gz.sha1 b/third_party/remapper.tar.gz.sha1
new file mode 100644
index 0000000..6600831
--- /dev/null
+++ b/third_party/remapper.tar.gz.sha1
@@ -0,0 +1 @@
+da2ce26c22a1d787fe436f1ea03d4eee6306bb35
\ No newline at end of file
diff --git a/third_party/retrace_benchmark.tar.gz.sha1 b/third_party/retrace_benchmark.tar.gz.sha1
new file mode 100644
index 0000000..7396a0f
--- /dev/null
+++ b/third_party/retrace_benchmark.tar.gz.sha1
@@ -0,0 +1 @@
+d73cd63dea729786d634bf3503db1795e72a20ec
\ No newline at end of file
diff --git a/tools/compare_apk_sizes.py b/tools/compare_apk_sizes.py
index 76ef1fc..da505c7 100755
--- a/tools/compare_apk_sizes.py
+++ b/tools/compare_apk_sizes.py
@@ -13,10 +13,10 @@
import sys
import threading
import time
+import zipfile
+
import toolhelper
import utils
-import zipfile
-import StringIO
USAGE = """%prog [options] app1 app2
NOTE: This only makes sense if minification is disabled"""
diff --git a/tools/download_all_benchmark_dependencies.py b/tools/download_all_benchmark_dependencies.py
index 1113370..95b92ed 100755
--- a/tools/download_all_benchmark_dependencies.py
+++ b/tools/download_all_benchmark_dependencies.py
@@ -19,11 +19,15 @@
utils.DownloadFromX20(
os.path.join(
utils.THIRD_PARTY, 'benchmarks', 'android-sdk') + '.tar.gz.sha1')
+ utils.DownloadFromX20(
+ os.path.join(utils.THIRD_PARTY, 'remapper') + '.tar.gz.sha1')
utils.DownloadFromGoogleCloudStorage(utils.SAMPLE_LIBRARIES_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.OPENSOURCE_APPS_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.ANDROID_SDK + '.tar.gz.sha1',
bucket='r8-deps-internal',
auth=True)
+ utils.DownloadFromGoogleCloudStorage(
+ os.path.join(utils.THIRD_PARTY, 'retrace_benchmark') + '.tar.gz.sha1')
if __name__ == '__main__':
sys.exit(Main())
diff --git a/tools/golem.py b/tools/golem.py
index 466c3ab..586ac5e 100755
--- a/tools/golem.py
+++ b/tools/golem.py
@@ -21,6 +21,8 @@
'proguard',
'proguardsettings',
'r8',
+ 'remapper',
+ 'retrace_benchmarks',
'sample_libraries',
'youtube',
]
diff --git a/tools/retrace_benchmark.py b/tools/retrace_benchmark.py
new file mode 100755
index 0000000..5e2bd3b
--- /dev/null
+++ b/tools/retrace_benchmark.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import jdk
+import golem
+import os
+import subprocess
+import sys
+import time
+import toolhelper
+import utils
+
+RETRACERS = ['r8', 'proguard', 'remapper']
+
+def parse_arguments(argv):
+ parser = argparse.ArgumentParser(
+ description = 'Run r8 retrace bootstrap benchmarks.')
+ parser.add_argument('--golem',
+ help = 'Link in third party dependencies.',
+ default = False,
+ action = 'store_true')
+ parser.add_argument('--ignore-java-version',
+ help='Do not check java version',
+ default=False,
+ action='store_true')
+ parser.add_argument('--print-runtimeraw',
+ metavar='BENCHMARKNAME',
+ help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
+ ' <elapsed> ms\' at the end where <elapsed> is' +
+ ' the elapsed time in milliseconds.')
+ parser.add_argument('--retracer',
+ help='The retracer to use',
+ choices=RETRACERS,
+ required=True)
+ options = parser.parse_args(argv)
+ return options
+
+
+def run_retrace(options, temp):
+ if options.retracer == 'r8':
+ retracer_args = [
+ '-cp', utils.R8LIB_JAR, 'com.android.tools.r8.retrace.Retrace']
+ elif options.retracer == 'proguard':
+ retracer_args = ['-jar',
+ os.path.join(
+ utils.THIRD_PARTY,
+ 'proguard',
+ 'proguard6.0.1',
+ 'lib',
+ 'retrace.jar')]
+ elif options.retracer == 'remapper':
+ retracer_args = ['-jar',
+ os.path.join(
+ utils.THIRD_PARTY,
+ 'remapper',
+ 'remapper_deploy.jar')]
+ else:
+ assert False, "Unexpected retracer " + options.retracer
+ retrace_args = [jdk.GetJavaExecutable()] + retracer_args + [
+ os.path.join(utils.THIRD_PARTY, 'retrace_benchmark', 'r8lib.jar.map'),
+ os.path.join(utils.THIRD_PARTY, 'retrace_benchmark', 'stacktrace.txt')]
+ utils.PrintCmd(retrace_args)
+ t0 = time.time()
+ subprocess.check_call(
+ retrace_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ t1 = time.time()
+ if options.print_runtimeraw:
+ print('{}(RunTimeRaw): {} ms'
+ .format(options.print_runtimeraw, 1000.0 * (t1 - t0)))
+
+
+if __name__ == '__main__':
+ options = parse_arguments(sys.argv[1:])
+ if options.golem:
+ golem.link_third_party()
+ if not options.ignore_java_version:
+ utils.check_java_version()
+ with utils.TempDir() as temp:
+ run_retrace(options, temp)
+
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 16db28d..07de70c 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -167,6 +167,9 @@
help='Include timing',
default=False,
action='store_true')
+ result.add_option('--cpu-list',
+ help='Run under \'taskset\' with these CPUs. See '
+ 'the \'taskset\' -c option for the format')
return result.parse_args(argv)
@@ -575,7 +578,9 @@
stdout=stdout,
stderr=stderr,
timeout=options.timeout,
- quiet=quiet)
+ quiet=quiet,
+ cmd_prefix=[
+ 'taskset', '-c', options.cpu_list] if options.cpu_list else [])
if exit_code != 0:
with open(stderr_path) as stderr:
stderr_text = stderr.read()
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 2784ea5..cc1a9cb 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -3,20 +3,24 @@
# BSD-style license that can be found in the LICENSE file.
import glob
-import gradle
-import jdk
import subprocess
from threading import Timer
+
+import gradle
+import jdk
import utils
+
def run(tool, args, build=None, debug=True,
profile=False, track_memory_file=None, extra_args=None,
- stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False):
+ stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False,
+ cmd_prefix=[]):
+ cmd = []
+ cmd.extend(cmd_prefix)
if build is None:
build, args = extract_build_from_args(args)
if build:
gradle.RunGradle(['r8lib' if tool.startswith('r8lib') else 'r8'])
- cmd = []
if track_memory_file:
cmd.extend(['tools/track_memory.sh', track_memory_file])
cmd.append(jdk.GetJavaExecutable())