Version 1.7.3-dev
Merge commit '02d1368bd0297f912df0f47d32f2ba639358f7a8' into 1.7
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index ce1b928..9eccf41 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -447,7 +447,7 @@
}
DesugaredLibraryConfiguration getDesugaredLibraryConfiguration(
- DexItemFactory factory, boolean libraryCompilation, int minAPILevel) {
+ DexItemFactory factory, boolean libraryCompilation) {
if (desugaredLibraryConfigurationResources.isEmpty()) {
return DesugaredLibraryConfiguration.empty();
}
@@ -456,22 +456,6 @@
}
StringResource desugaredLibraryConfigurationResource =
desugaredLibraryConfigurationResources.get(0);
-
- // TODO(b/134732760): Remove the following once the default testing hack is not supported
- // anymore.
- try {
- if (desugaredLibraryConfigurationResource.getString().equals("default")) {
- if (libraryCompilation) {
- return DesugaredLibraryConfigurationForTesting
- .configureLibraryDesugaringForLibraryCompilation(minAPILevel, factory);
- }
- return DesugaredLibraryConfigurationForTesting
- .configureLibraryDesugaringForProgramCompilation(minAPILevel, factory);
- }
- } catch (ResourceException e) {
- throw new RuntimeException(e);
- }
-
DesugaredLibraryConfigurationParser libraryParser =
new DesugaredLibraryConfigurationParser(
factory, getReporter(), libraryCompilation, getMinApiLevel());
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d6ca3f2..b72548b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -201,7 +201,7 @@
DexItemFactory factory = new DexItemFactory();
DesugaredLibraryConfiguration libraryConfiguration =
- getDesugaredLibraryConfiguration(factory, false, getMinApiLevel());
+ getDesugaredLibraryConfiguration(factory, false);
return new D8Command(
getAppBuilder().build(),
diff --git a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java b/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
deleted file mode 100644
index 2f03241..0000000
--- a/src/main/java/com/android/tools/r8/DesugaredLibraryConfigurationForTesting.java
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.util.List;
-import java.util.Map;
-
-// TODO(134732760): This is still work in progress.
-// The DesugaredLibraryConfigurationForTesting is a set of flags for experimentation
-// with the desugared JDK libraries which should be replaced by a D8/R8
-// API level flag --coreLibraryDescriptor
-public class DesugaredLibraryConfigurationForTesting {
-
- private static Map<String, String> buildPrefixRewritingForProgramCompilationAllAndroid() {
- return ImmutableMap.<String, String>builder()
- .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
- .put("java.util.stream.", "j$.util.stream.")
- .put("java.util.function.", "j$.util.function.")
- .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
- .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
- .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
- .put("java.util.Optional", "j$.util.Optional")
- .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
- .put("java.util.Spliterator", "j$.util.Spliterator")
- .put("java.util.StringJoiner", "j$.util.StringJoiner")
- .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
- .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
- .putAll(buildPrefixRewritingForProgramCompilationAndroidNPlus())
- .build();
- }
-
- private static Map<String, String> buildPrefixRewritingForProgramCompilationAndroidNPlus() {
- return ImmutableMap.<String, String>builder()
- .put("java.time.", "j$.time.")
- .put("java.util.Desugar", "j$.util.Desugar")
- .build();
- }
-
- private static Map<String, String>
- buildRetargetCoreLibraryMemberForProgramCompilationAllAndroid() {
- return ImmutableMap.<String, String>builder()
- .put("java.util.Arrays#stream", "java.util.DesugarArrays")
- .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
- .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
- .put(
- "java.util.concurrent.atomic.AtomicInteger#getAndUpdate",
- "java.util.concurrent.atomic.DesugarAtomicInteger")
- .put(
- "java.util.concurrent.atomic.AtomicInteger#updateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicInteger")
- .put(
- "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate",
- "java.util.concurrent.atomic.DesugarAtomicInteger")
- .put(
- "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicInteger")
- .put(
- "java.util.concurrent.atomic.AtomicLong#getAndUpdate",
- "java.util.concurrent.atomic.DesugarAtomicLong")
- .put(
- "java.util.concurrent.atomic.AtomicLong#updateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicLong")
- .put(
- "java.util.concurrent.atomic.AtomicLong#getAndAccumulate",
- "java.util.concurrent.atomic.DesugarAtomicLong")
- .put(
- "java.util.concurrent.atomic.AtomicLong#accumulateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicLong")
- .put(
- "java.util.concurrent.atomic.AtomicReference#getAndUpdate",
- "java.util.concurrent.atomic.DesugarAtomicReference")
- .put(
- "java.util.concurrent.atomic.AtomicReference#updateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicReference")
- .put(
- "java.util.concurrent.atomic.AtomicReference#getAndAccumulate",
- "java.util.concurrent.atomic.DesugarAtomicReference")
- .put(
- "java.util.concurrent.atomic.AtomicReference#accumulateAndGet",
- "java.util.concurrent.atomic.DesugarAtomicReference")
- .putAll(buildRetargetCoreLibraryMemberForProgramCompilationAndroidNPlus())
- .build();
- }
-
- private static Map<String, String>
- buildRetargetCoreLibraryMemberForProgramCompilationAndroidNPlus() {
- // --retarget_core_library_member.
- return ImmutableMap.<String, String>builder()
- .put("java.util.Calendar#toInstant", "java.util.DesugarCalendar")
- .put("java.util.Date#from", "java.util.DesugarDate")
- .put("java.util.Date#toInstant", "java.util.DesugarDate")
- .put("java.util.GregorianCalendar#from", "java.util.DesugarGregorianCalendar")
- .put("java.util.GregorianCalendar#toZonedDateTime", "java.util.DesugarGregorianCalendar")
- .build();
- }
-
- private static Map<String, String> buildPrefixRewritingForCoreLibCompilationAllAndroid() {
- return ImmutableMap.<String, String>builder()
- .put("java.util.concurrent.ConcurrentHashMap", "j$.util.concurrent.ConcurrentHashMap")
- .put("java.util.stream.", "j$.util.stream.")
- .put("java.util.function.", "j$.util.function.")
- .put("java.util.Comparators", "j$.util.Comparators")
- .put("java.util.DoubleSummaryStatistics", "j$.util.DoubleSummaryStatistics")
- .put("java.util.IntSummaryStatistics", "j$.util.IntSummaryStatistics")
- .put("java.util.LongSummaryStatistics", "j$.util.LongSummaryStatistics")
- .put("java.util.Objects", "j$.util.Objects")
- .put("java.util.Optional", "j$.util.Optional")
- .put("java.util.PrimitiveIterator", "j$.util.PrimitiveIterator")
- .put("java.util.SortedSet$1", "j$.util.SortedSet$1")
- .put("java.util.Spliterator", "j$.util.Spliterator")
- .put("java.util.StringJoiner", "j$.util.StringJoiner")
- .put("java.util.Tripwire", "j$.util.Tripwire")
- .put("java.util.concurrent.DesugarUnsafe", "j$.util.concurrent.DesugarUnsafe")
- .put("java.util.concurrent.ThreadLocalRandom", "j$.util.concurrent.ThreadLocalRandom")
- .put("java.util.concurrent.atomic.DesugarAtomic", "j$.util.concurrent.atomic.DesugarAtomic")
- .putAll(buildPrefixRewritingForCoreLibCompilationAndroidNPlus())
- .build();
- }
-
- private static Map<String, String> buildPrefixRewritingForCoreLibCompilationAndroidNPlus() {
- return ImmutableMap.<String, String>builder()
- .put("j$.time.", "java.time.")
- .put("java.time.", "j$.time.")
- .put("java.util.Desugar", "j$.util.Desugar")
- .build();
- }
-
- private static Map<String, String> buildRetargetCoreLibraryMemberForCoreLibCompilation() {
- return ImmutableMap.<String, String>builder()
- .put("java.util.LinkedHashSet#spliterator", "java.util.DesugarLinkedHashSet")
- // Following 2 for testing only (core library with extensions).
- .put("java.util.Arrays#stream", "java.util.DesugarArrays")
- .put("java.util.Arrays#spliterator", "java.util.DesugarArrays")
- .build();
- }
-
- private static List<String> buildDontRewriteInvocations() {
- return ImmutableList.of("java.util.Iterator#remove");
- }
-
- private static Map<String, String> buildEmulateLibraryInterface() {
- return ImmutableMap.<String, String>builder()
- .put("java.util.Map$Entry", "j$.util.Map$Entry")
- .put("java.util.Collection", "j$.util.Collection")
- .put("java.util.Map", "j$.util.Map")
- .put("java.util.Iterator", "j$.util.Iterator")
- .put("java.util.Comparator", "j$.util.Comparator")
- // Extra flags: in R8 we marked as emulated all interfaces
- // with default methods. Emulated interfaces have their
- // companion class moved to j$ and have a dispatch class.
- // Bazel instead analyzes the class hierarchy.
- .put("java.util.List", "j$.util.List")
- .put("java.util.SortedSet", "j$.util.SortedSet")
- .put("java.util.Set", "j$.util.Set")
- .put("java.util.concurrent.ConcurrentMap", "j$.util.concurrent.ConcurrentMap")
- .build();
- }
-
- private static Map<String, String> buildBackportCoreLibraryMembers() {
- // R8 specific to deal with *8 removal.
- return ImmutableMap.<String, String>builder()
- .put("java.lang.Double8", "java.lang.Double")
- .put("java.lang.Integer8", "java.lang.Integer")
- .put("java.lang.Long8", "java.lang.Long")
- .put("java.lang.Math8", "java.lang.Math")
- .build();
- }
-
- public static DesugaredLibraryConfiguration configureLibraryDesugaringForProgramCompilation(
- int minApiLevel, DexItemFactory factory) {
- if (minApiLevel >= AndroidApiLevel.O.getLevel()) {
- return DesugaredLibraryConfiguration.empty();
- }
- Map<String, String> rewritePrefix;
- Map<String, String> retargetCoreLibMember;
- Map<String, String> emulateLibraryInterface = ImmutableMap.of();
- List<String> dontRewriteInvocations = ImmutableList.of();
- if (minApiLevel < AndroidApiLevel.N.getLevel()) {
- rewritePrefix = buildPrefixRewritingForProgramCompilationAllAndroid();
- retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilationAllAndroid();
- emulateLibraryInterface = buildEmulateLibraryInterface();
- dontRewriteInvocations = buildDontRewriteInvocations();
- } else {
- rewritePrefix = buildPrefixRewritingForProgramCompilationAndroidNPlus();
- retargetCoreLibMember = buildRetargetCoreLibraryMemberForProgramCompilationAndroidNPlus();
- }
- return createDesugaredLibraryConfiguration(
- factory,
- false,
- rewritePrefix,
- emulateLibraryInterface,
- retargetCoreLibMember,
- ImmutableMap.of(),
- dontRewriteInvocations);
- }
-
- public static DesugaredLibraryConfiguration configureLibraryDesugaringForLibraryCompilation(
- int minApiLevel, DexItemFactory factory) {
- if (minApiLevel >= AndroidApiLevel.O.getLevel()) {
- return DesugaredLibraryConfiguration.empty();
- }
- Map<String, String> rewritePrefix;
- Map<String, String> retargetCoreLibMember = ImmutableMap.of();
- Map<String, String> emulateLibraryInterface = ImmutableMap.of();
- List<String> dontRewriteInvocations = ImmutableList.of();
- Map<String, String> backportCoreLibraryMembers = buildBackportCoreLibraryMembers();
- if (minApiLevel < AndroidApiLevel.N.getLevel()) {
- retargetCoreLibMember = buildRetargetCoreLibraryMemberForCoreLibCompilation();
- dontRewriteInvocations = buildDontRewriteInvocations();
- emulateLibraryInterface = buildEmulateLibraryInterface();
- rewritePrefix = buildPrefixRewritingForCoreLibCompilationAllAndroid();
- } else {
- rewritePrefix = buildPrefixRewritingForCoreLibCompilationAndroidNPlus();
- }
- return createDesugaredLibraryConfiguration(
- factory,
- true,
- rewritePrefix,
- emulateLibraryInterface,
- retargetCoreLibMember,
- backportCoreLibraryMembers,
- dontRewriteInvocations);
- }
-
- public static DesugaredLibraryConfiguration createDesugaredLibraryConfiguration(
- DexItemFactory factory,
- boolean libraryCompilation,
- Map<String, String> rewritePrefix,
- Map<String, String> emulateLibraryInterface,
- Map<String, String> retargetCoreLibMember,
- Map<String, String> backportCoreLibraryMembers,
- List<String> dontRewriteInvocations) {
- DesugaredLibraryConfiguration.Builder builder = DesugaredLibraryConfiguration.builder(factory);
- if (libraryCompilation) {
- builder.setLibraryCompilation();
- } else {
- builder.setProgramCompilation();
- }
- for (String key : rewritePrefix.keySet()) {
- builder.putRewritePrefix(key, rewritePrefix.get(key));
- }
- for (String key : emulateLibraryInterface.keySet()) {
- builder.putEmulateLibraryInterface(key, emulateLibraryInterface.get(key));
- }
- for (String key : retargetCoreLibMember.keySet()) {
- builder.putRetargetCoreLibMember(key, retargetCoreLibMember.get(key));
- }
- for (String key : backportCoreLibraryMembers.keySet()) {
- builder.putBackportCoreLibraryMember(key, backportCoreLibraryMembers.get(key));
- }
- for (String key : dontRewriteInvocations) {
- builder.addDontRewriteInvocation(key);
- }
- return builder.build();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index d20afac..d1a8e6a 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -213,7 +213,7 @@
DexItemFactory factory = new DexItemFactory();
DesugaredLibraryConfiguration libraryConfiguration =
- getDesugaredLibraryConfiguration(factory, true, getMinApiLevel());
+ getDesugaredLibraryConfiguration(factory, true);
R8Command r8Command = null;
D8Command d8Command = null;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index c986011..5c90ae8 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -447,7 +447,7 @@
}
DesugaredLibraryConfiguration libraryConfiguration =
- getDesugaredLibraryConfiguration(factory, false, getMinApiLevel());
+ getDesugaredLibraryConfiguration(factory, false);
ProguardConfigurationParser parser =
new ProguardConfigurationParser(factory, reporter, allowTestProguardOptions);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 52963e9..0b02763 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.7.1-dev";
+ public static final String LABEL = "1.7.3-dev";
private Version() {
}
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 77a5da9..b626e56 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
@@ -46,6 +47,7 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.ObjectArrays;
@@ -388,46 +390,15 @@
options.reporter, options.mainDexListConsumer, writeMainDexList(application, namingLens));
ExceptionUtils.withFinishedResourceHandler(options.reporter, options.mainDexListConsumer);
}
+
DataResourceConsumer dataResourceConsumer = options.dataResourceConsumer;
if (dataResourceConsumer != null) {
+ ImmutableList<DataResourceProvider> dataResourceProviders = application.dataResourceProviders;
ResourceAdapter resourceAdapter =
new ResourceAdapter(appView, application.dexItemFactory, graphLense, namingLens, options);
- Set<String> generatedResourceNames = new HashSet<>();
- for (DataResourceProvider dataResourceProvider : application.dataResourceProviders) {
- try {
- dataResourceProvider.accept(
- new Visitor() {
- @Override
- public void visit(DataDirectoryResource directory) {
- DataDirectoryResource adapted = resourceAdapter.adaptIfNeeded(directory);
- if (adapted != null) {
- dataResourceConsumer.accept(adapted, options.reporter);
- options.reporter.failIfPendingErrors();
- }
- }
-
- @Override
- public void visit(DataEntryResource file) {
- if (resourceAdapter.isService(file)) {
- // META-INF/services resources are handled below.
- return;
- }
-
- DataEntryResource adapted = resourceAdapter.adaptIfNeeded(file);
- if (generatedResourceNames.add(adapted.getName())) {
- dataResourceConsumer.accept(adapted, options.reporter);
- } else {
- options.reporter.warning(
- new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
- }
- options.reporter.failIfPendingErrors();
- }
- });
- } catch (ResourceException e) {
- throw new CompilationError(e.getMessage(), e);
- }
- }
+ adaptAndPassDataResources(
+ options, dataResourceConsumer, dataResourceProviders, resourceAdapter);
// Write the META-INF/services resources. Sort on service names and keep the order from
// the input for the implementation lines for deterministic output.
@@ -454,6 +425,60 @@
});
}
}
+
+ if (options.featureSplitConfiguration != null) {
+ for (DataResourceProvidersAndConsumer entry :
+ options.featureSplitConfiguration.getDataResourceProvidersAndConsumers()) {
+ ResourceAdapter resourceAdapter =
+ new ResourceAdapter(
+ appView, application.dexItemFactory, graphLense, namingLens, options);
+ adaptAndPassDataResources(
+ options, entry.getConsumer(), entry.getProviders(), resourceAdapter);
+ }
+ }
+ }
+
+ private static void adaptAndPassDataResources(
+ InternalOptions options,
+ DataResourceConsumer dataResourceConsumer,
+ Collection<DataResourceProvider> dataResourceProviders,
+ ResourceAdapter resourceAdapter) {
+ Set<String> generatedResourceNames = new HashSet<>();
+
+ for (DataResourceProvider dataResourceProvider : dataResourceProviders) {
+ try {
+ dataResourceProvider.accept(
+ new Visitor() {
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ DataDirectoryResource adapted = resourceAdapter.adaptIfNeeded(directory);
+ if (adapted != null) {
+ dataResourceConsumer.accept(adapted, options.reporter);
+ options.reporter.failIfPendingErrors();
+ }
+ }
+
+ @Override
+ public void visit(DataEntryResource file) {
+ if (resourceAdapter.isService(file)) {
+ // META-INF/services resources are handled below.
+ return;
+ }
+
+ DataEntryResource adapted = resourceAdapter.adaptIfNeeded(file);
+ if (generatedResourceNames.add(adapted.getName())) {
+ dataResourceConsumer.accept(adapted, options.reporter);
+ } else {
+ options.reporter.warning(
+ new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
+ }
+ options.reporter.failIfPendingErrors();
+ }
+ });
+ } catch (ResourceException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ }
}
private static boolean validateProguardMapParses(String content) {
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index 22e819e..a122ad7 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.features;
+import com.android.tools.r8.DataResourceConsumer;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResourceProvider;
@@ -14,7 +16,10 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -59,6 +64,49 @@
return result;
}
+ public static class DataResourceProvidersAndConsumer {
+ private final Set<DataResourceProvider> providers;
+ private final DataResourceConsumer consumer;
+
+ public DataResourceProvidersAndConsumer(
+ Set<DataResourceProvider> providers, DataResourceConsumer consumer) {
+ this.providers = providers;
+ this.consumer = consumer;
+ }
+
+ public Set<DataResourceProvider> getProviders() {
+ return providers;
+ }
+
+ public DataResourceConsumer getConsumer() {
+ return consumer;
+ }
+ }
+
+ public Collection<DataResourceProvidersAndConsumer> getDataResourceProvidersAndConsumers() {
+ List<DataResourceProvidersAndConsumer> result = new ArrayList<>();
+ for (FeatureSplit featureSplit : featureSplits) {
+ DataResourceConsumer dataResourceConsumer =
+ featureSplit.getProgramConsumer().getDataResourceConsumer();
+ if (dataResourceConsumer != null) {
+ Set<DataResourceProvider> dataResourceProviders = new HashSet<>();
+ for (ProgramResourceProvider programResourceProvider :
+ featureSplit.getProgramResourceProviders()) {
+ DataResourceProvider dataResourceProvider =
+ programResourceProvider.getDataResourceProvider();
+ if (dataResourceProvider != null) {
+ dataResourceProviders.add(dataResourceProvider);
+ }
+ }
+ if (!dataResourceProviders.isEmpty()) {
+ result.add(
+ new DataResourceProvidersAndConsumer(dataResourceProviders, dataResourceConsumer));
+ }
+ }
+ }
+ return result;
+ }
+
public boolean isInFeature(DexProgramClass clazz) {
return javaTypeToFeatureSplitMapping.containsKey(
DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 9a71dc7..e3757da 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -33,6 +33,7 @@
}
public Value getReceiver() {
+ assert inValues.size() > 0;
return inValues.get(0);
}
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 a75ce9e..46f8bf4 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
@@ -1317,26 +1317,6 @@
assert code.isConsistentSSA();
}
- if (nonNullTracker != null) {
- // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
- // this may not be the right place to collect call site optimization info.
- // Collecting call-site optimization info depends on the existence of non-null IRs.
- // Arguments can be changed during the debug mode.
- if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
- appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
- }
- // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
- nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
- }
- if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
- codeRewriter.removeAssumeInstructions(code);
- assert code.isConsistentSSA();
- }
- // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
- assert code.verifyNoNullabilityBottomTypes();
-
- assert code.verifyTypes(appView);
-
previous = printMethod(code, "IR after outline handler (SSA)", previous);
// TODO(mkroghj) Test if shorten live ranges is worth it.
@@ -1362,6 +1342,26 @@
classStaticizer.examineMethodCode(method, code);
}
+ if (nonNullTracker != null) {
+ // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+ // this may not be the right place to collect call site optimization info.
+ // Collecting call-site optimization info depends on the existence of non-null IRs.
+ // Arguments can be changed during the debug mode.
+ if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+ appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+ }
+ // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+ nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+ }
+ if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+ codeRewriter.removeAssumeInstructions(code);
+ assert code.isConsistentSSA();
+ }
+ // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+ assert code.verifyNoNullabilityBottomTypes();
+
+ assert code.verifyTypes(appView);
+
if (appView.enableWholeProgramOptimizations()) {
if (libraryMethodOverrideAnalysis != null) {
libraryMethodOverrideAnalysis.analyze(code);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index b796600..f6f34e6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1056,6 +1056,12 @@
addProvider(
new MethodGenerator(
method, BackportedMethods::CollectionMethods_mapOfEntries, "ofEntries"));
+
+ // Map.Entry<K, V> Map.entry(K, V)
+ type = factory.mapType;
+ proto = factory.createProto(factory.mapEntryType, factory.objectType, factory.objectType);
+ method = factory.createMethod(type, proto, "entry");
+ addProvider(new MethodGenerator(method, BackportedMethods::CollectionMethods_mapEntry));
}
private void initializeJava11MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 0922cd2..9f22ded 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -19,9 +19,12 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
// TODO(b/134732760): In progress.
// I convert library calls with desugared parameters/return values so they can work normally.
@@ -72,13 +75,17 @@
if (dexClass == null || !dexClass.isLibraryClass()) {
continue;
}
+ // Library methods do not understand desugared types, hence desugared types have to be
+ // converted around non desugared library calls for the invoke to resolve.
if (appView.rewritePrefix.hasRewrittenType(invokedMethod.proto.returnType)) {
- addReturnConversion(code, invokeMethod, iterator);
+ rewriteLibraryInvoke(code, invokeMethod, iterator);
+ continue;
}
for (int i = 0; i < invokedMethod.proto.parameters.values.length; i++) {
DexType argType = invokedMethod.proto.parameters.values[i];
if (appView.rewritePrefix.hasRewrittenType(argType)) {
- addParameterConversion(code, invokeMethod, iterator, argType, i);
+ rewriteLibraryInvoke(code, invokeMethod, iterator);
+ continue;
}
}
}
@@ -109,117 +116,126 @@
return vivifiedType;
}
- private void addParameterConversion(
- IRCode code,
- InvokeMethod invokeMethod,
- InstructionListIterator iterator,
- DexType argType,
- int parameter) {
- if (!appView
- .options()
- .desugaredLibraryConfiguration
- .getCustomConversions()
- .containsKey(argType)) {
- // TODO(b/134732760): Add Wrapper Conversions.
- warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter");
- return;
- }
-
- Value inValue = invokeMethod.inValues().get(parameter);
- DexType argVivifiedType = vivifiedTypeFor(argType);
- DexType conversionHolder =
- appView.options().desugaredLibraryConfiguration.getCustomConversions().get(argType);
-
- // ConversionType has static method "type convert(rewrittenType)".
- // But everything is going to be rewritten, so we need to call "vivifiedType convert(type)".
- DexMethod conversionMethod =
- factory.createMethod(
- conversionHolder,
- factory.createProto(argVivifiedType, argType),
- factory.convertMethodName);
- Value convertedValue =
- code.createValue(
- TypeLatticeElement.fromDexType(
- argVivifiedType, inValue.getTypeLattice().nullability(), appView));
- InvokeStatic conversionInstruction =
- new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(inValue));
- conversionInstruction.setPosition(invokeMethod.getPosition());
- iterator.previous();
- iterator.add(conversionInstruction);
- iterator.next();
-
- // Rewrite invoke (signature and inValue to rewrite).
- DexMethod newDexMethod =
- dexMethodWithDifferentParameter(
- invokeMethod.getInvokedMethod(), argVivifiedType, parameter);
- Invoke newInvokeMethod =
- Invoke.create(
- invokeMethod.getType(),
- newDexMethod,
- newDexMethod.proto,
- invokeMethod.outValue(),
- invokeMethod.inValues());
- newInvokeMethod.replaceValue(parameter, conversionInstruction.outValue());
- iterator.replaceCurrentInstruction(newInvokeMethod);
- }
-
- private void addReturnConversion(
+ private void rewriteLibraryInvoke(
IRCode code, InvokeMethod invokeMethod, InstructionListIterator iterator) {
- DexType returnType = invokeMethod.getReturnType();
- if (!appView
- .options()
- .desugaredLibraryConfiguration
- .getCustomConversions()
- .containsKey(returnType)) {
- // TODO(b/134732760): Add Wrapper Conversions.
- warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return");
- return;
+ DexMethod invokedMethod = invokeMethod.getInvokedMethod();
+
+ // Create return conversion if required.
+ Instruction returnConversion = null;
+ DexType newReturnType;
+ DexType returnType = invokedMethod.proto.returnType;
+ if (appView.rewritePrefix.hasRewrittenType(returnType)) {
+ if (appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getCustomConversions()
+ .containsKey(returnType)) {
+ newReturnType = vivifiedTypeFor(returnType);
+ // Return conversion added only if return value is used.
+ if (invokeMethod.outValue() != null
+ && invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers()
+ > 0) {
+ returnConversion =
+ createReturnConversionAndReplaceUses(code, invokeMethod, returnType, newReturnType);
+ }
+ } else {
+ // TODO(b/134732760): Add Wrapper Conversions.
+ warnInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return");
+ newReturnType = returnType;
+ }
+ } else {
+ newReturnType = returnType;
}
- DexType returnVivifiedType = vivifiedTypeFor(returnType);
- DexType conversionHolder =
- appView.options().desugaredLibraryConfiguration.getCustomConversions().get(returnType);
+ // Create parameter conversions if required.
+ List<Instruction> parameterConversions = new ArrayList<>();
+ List<Value> newInValues = new ArrayList<>();
+ if (invokeMethod.isInvokeMethodWithReceiver()) {
+ assert !appView.rewritePrefix.hasRewrittenType(invokedMethod.holder);
+ newInValues.add(invokeMethod.asInvokeMethodWithReceiver().getReceiver());
+ }
+ int receiverShift = BooleanUtils.intValue(invokeMethod.isInvokeMethodWithReceiver());
+ DexType[] parameters = invokedMethod.proto.parameters.values;
+ DexType[] newParameters = parameters.clone();
+ for (int i = 0; i < parameters.length; i++) {
+ DexType argType = parameters[i];
+ if (appView.rewritePrefix.hasRewrittenType(argType)) {
+ if (appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getCustomConversions()
+ .containsKey(argType)) {
+ DexType argVivifiedType = vivifiedTypeFor(argType);
+ Value inValue = invokeMethod.inValues().get(i + receiverShift);
+ newParameters[i] = argVivifiedType;
+ parameterConversions.add(
+ createParameterConversion(code, argType, argVivifiedType, inValue));
+ newInValues.add(parameterConversions.get(parameterConversions.size() - 1).outValue());
+ } else {
+ // TODO(b/134732760): Add Wrapper Conversions.
+ warnInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter");
+ newInValues.add(invokeMethod.inValues().get(i + receiverShift));
+ }
+ } else {
+ newInValues.add(invokeMethod.inValues().get(i + receiverShift));
+ }
+ }
- // ConversionType has static method "rewrittenType convert(type)".
- // But everything is going to be rewritten, so we need to call "type convert(vivifiedType)".
- DexMethod conversionMethod =
- factory.createMethod(
- conversionHolder,
- factory.createProto(returnType, returnVivifiedType),
- factory.convertMethodName);
- Value convertedValue =
- code.createValue(
- TypeLatticeElement.fromDexType(returnType, Nullability.maybeNull(), appView));
- invokeMethod.outValue().replaceUsers(convertedValue);
- InvokeStatic conversionInstruction =
- new InvokeStatic(
- conversionMethod, convertedValue, Collections.singletonList(invokeMethod.outValue()));
- conversionInstruction.setPosition(invokeMethod.getPosition());
-
- // Rewrite invoke (signature to rewrite).
+ // Patch the invoke with new types and new inValues.
+ DexProto newProto = factory.createProto(newReturnType, newParameters);
DexMethod newDexMethod =
- dexMethodWithDifferentReturn(invokeMethod.getInvokedMethod(), returnVivifiedType);
+ factory.createMethod(invokedMethod.holder, newProto, invokedMethod.name);
Invoke newInvokeMethod =
Invoke.create(
invokeMethod.getType(),
newDexMethod,
newDexMethod.proto,
invokeMethod.outValue(),
- invokeMethod.inValues());
+ newInValues);
+
+ // Insert and reschedule all instructions.
+ iterator.previous();
+ for (Instruction parameterConversion : parameterConversions) {
+ parameterConversion.setPosition(invokeMethod.getPosition());
+ iterator.add(parameterConversion);
+ }
+ assert iterator.peekNext() == invokeMethod;
+ iterator.next();
iterator.replaceCurrentInstruction(newInvokeMethod);
- iterator.add(conversionInstruction);
+ if (returnConversion != null) {
+ returnConversion.setPosition(invokeMethod.getPosition());
+ iterator.add(returnConversion);
+ }
}
- private DexMethod dexMethodWithDifferentParameter(
- DexMethod method, DexType newParameterType, int parameter) {
- DexType[] newParameters = method.proto.parameters.values.clone();
- newParameters[parameter] = newParameterType;
- DexProto newProto = factory.createProto(method.proto.returnType, newParameters);
- return factory.createMethod(method.holder, newProto, method.name);
+ private Instruction createParameterConversion(
+ IRCode code, DexType argType, DexType argVivifiedType, Value inValue) {
+ DexMethod conversionMethod = createConversionMethod(argType, argType, argVivifiedType);
+ // The value is null only if the input is null.
+ Value convertedValue =
+ createConversionValue(code, inValue.getTypeLattice().nullability(), argVivifiedType);
+ return new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(inValue));
}
- private DexMethod dexMethodWithDifferentReturn(DexMethod method, DexType newReturnType) {
- DexProto newProto = factory.createProto(newReturnType, method.proto.parameters.values);
- return factory.createMethod(method.holder, newProto, method.name);
+ private Instruction createReturnConversionAndReplaceUses(
+ IRCode code, InvokeMethod invokeMethod, DexType returnType, DexType returnVivifiedType) {
+ DexMethod conversionMethod = createConversionMethod(returnType, returnVivifiedType, returnType);
+ Value convertedValue = createConversionValue(code, Nullability.maybeNull(), returnType);
+ invokeMethod.outValue().replaceUsers(convertedValue);
+ return new InvokeStatic(
+ conversionMethod, convertedValue, Collections.singletonList(invokeMethod.outValue()));
+ }
+
+ private DexMethod createConversionMethod(DexType type, DexType srcType, DexType destType) {
+ // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
+ // But everything is going to be rewritten, so we need to use vivifiedType and type".
+ DexType conversionHolder =
+ appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
+ return factory.createMethod(
+ conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+ }
+
+ private Value createConversionValue(IRCode code, Nullability nullability, DexType valueType) {
+ return code.createValue(TypeLatticeElement.fromDexType(valueType, nullability, appView));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index aadf9e5..864099b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -597,6 +597,60 @@
ImmutableList.of());
}
+ public static CfCode CollectionMethods_mapEntry(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfNew(
+ options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ label1,
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfLoad(ValueType.OBJECT, 1),
+ label2,
+ new CfInvoke(
+ 184,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/Objects;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("requireNonNull")),
+ false),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/util/AbstractMap$SimpleImmutableEntry;"),
+ options.itemFactory.createProto(
+ options.itemFactory.createType("V"),
+ options.itemFactory.createType("Ljava/lang/Object;"),
+ options.itemFactory.createType("Ljava/lang/Object;")),
+ options.itemFactory.createString("<init>")),
+ false),
+ label3,
+ new CfReturn(ValueType.OBJECT),
+ label4),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode CollectionMethods_mapOfEntries(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 0008162..45f8c45 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -5,7 +5,9 @@
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.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Assume;
@@ -89,6 +91,15 @@
continue;
}
if (instruction.isInvokeMethod()) {
+ // For virtual and interface calls, proceed on valid results only (since it's enforced).
+ if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
+ DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
+ ResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(invokedMethod.holder, invokedMethod);
+ if (!resolutionResult.isValidVirtualTarget(appView.options())) {
+ continue;
+ }
+ }
Collection<DexEncodedMethod> targets =
instruction.asInvokeMethod().lookupTargets(appView, context.method.holder);
if (targets == null) {
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 88a20da..1e7d21a 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
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.Arrays;
@@ -43,6 +44,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
+import java.util.function.Predicate;
public final class ClassStaticizer {
@@ -230,8 +232,8 @@
// We are inside an instance method of candidate class (not an instance initializer
// which we will check later), check if all the references to 'this' are valid
// (the call will invalidate the candidate if some of them are not valid).
- analyzeAllValueUsers(receiverClassCandidateInfo,
- receiverValue, factory.isConstructor(method.method));
+ analyzeAllValueUsers(
+ receiverClassCandidateInfo, receiverValue, factory.isConstructor(method.method));
// If the candidate is still valid, ignore all instructions
// we treat as valid usages on receiver.
@@ -289,7 +291,7 @@
// If the candidate still valid, ignore all usages in further analysis.
Value value = instruction.outValue();
if (value != null) {
- alreadyProcessed.addAll(value.uniqueUsers());
+ alreadyProcessed.addAll(value.aliasedUsers());
}
}
continue;
@@ -433,14 +435,16 @@
appView.appInfo().lookupDirectTarget(invoke.getInvokedMethod());
List<Value> values = invoke.inValues();
- if (values.lastIndexOf(candidateValue) != 0 ||
- methodInvoked == null || methodInvoked.method.holder != candidateType) {
+ if (ListUtils.lastIndexMatching(values, v -> v.getAliasedValue() == candidateValue) != 0
+ || methodInvoked == null
+ || methodInvoked.method.holder != candidateType) {
return false;
}
// Check arguments.
for (int i = 1; i < values.size(); i++) {
- if (!values.get(i).definition.isConstInstruction()) {
+ Value arg = values.get(i).getAliasedValue();
+ if (arg.isPhi() || !arg.definition.isConstInstruction()) {
return false;
}
}
@@ -484,40 +488,54 @@
private CandidateInfo analyzeAllValueUsers(
CandidateInfo candidateInfo, Value value, boolean ignoreSuperClassInitInvoke) {
- assert value != null;
+ assert value != null && value == value.getAliasedValue();
if (value.numberOfPhiUsers() > 0) {
return candidateInfo.invalidate();
}
- for (Instruction user : value.uniqueUsers()) {
- if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
- InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
- DexMethod methodReferenced = invoke.getInvokedMethod();
- if (factory.isConstructor(methodReferenced)) {
- assert user.isInvokeDirect();
- if (ignoreSuperClassInitInvoke &&
- invoke.inValues().lastIndexOf(value) == 0 &&
- methodReferenced == factory.objectMethods.constructor) {
- // If we are inside candidate constructor and analyzing usages
- // of the receiver, we want to ignore invocations of superclass
- // constructor which will be removed after staticizing.
- continue;
+ Set<Instruction> currentUsers = value.uniqueUsers();
+ while (!currentUsers.isEmpty()) {
+ Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
+ for (Instruction user : currentUsers) {
+ if (user.isAssume()) {
+ if (user.outValue().numberOfPhiUsers() > 0) {
+ return candidateInfo.invalidate();
}
- return candidateInfo.invalidate();
- }
- AppInfo appInfo = appView.appInfo();
- DexEncodedMethod methodInvoked = user.isInvokeDirect()
- ? appInfo.lookupDirectTarget(methodReferenced)
- : appInfo.lookupVirtualTarget(methodReferenced.holder, methodReferenced);
- if (invoke.inValues().lastIndexOf(value) == 0 &&
- methodInvoked != null && methodInvoked.method.holder == candidateInfo.candidate.type) {
+ indirectUsers.addAll(user.outValue().uniqueUsers());
continue;
}
- }
+ if (user.isInvokeVirtual() || user.isInvokeDirect() /* private methods */) {
+ InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
+ Predicate<Value> isAliasedValue = v -> v.getAliasedValue() == value;
+ DexMethod methodReferenced = invoke.getInvokedMethod();
+ if (factory.isConstructor(methodReferenced)) {
+ assert user.isInvokeDirect();
+ if (ignoreSuperClassInitInvoke
+ && ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
+ && methodReferenced == factory.objectMethods.constructor) {
+ // If we are inside candidate constructor and analyzing usages
+ // of the receiver, we want to ignore invocations of superclass
+ // constructor which will be removed after staticizing.
+ continue;
+ }
+ return candidateInfo.invalidate();
+ }
+ AppInfo appInfo = appView.appInfo();
+ DexEncodedMethod methodInvoked = user.isInvokeDirect()
+ ? appInfo.lookupDirectTarget(methodReferenced)
+ : appInfo.lookupVirtualTarget(methodReferenced.holder, methodReferenced);
+ if (ListUtils.lastIndexMatching(invoke.inValues(), isAliasedValue) == 0
+ && methodInvoked != null
+ && methodInvoked.method.holder == candidateInfo.candidate.type) {
+ continue;
+ }
+ }
- // All other users are not allowed.
- return candidateInfo.invalidate();
+ // All other users are not allowed.
+ return candidateInfo.invalidate();
+ }
+ currentUsers = indirectUsers;
}
return candidateInfo;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 1ab806d..3956609 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -340,10 +340,11 @@
}
Set<Phi> chainedPhis = Sets.newIdentityHashSet();
for (Value operand : phi.getOperands()) {
- if (operand.isPhi()) {
+ Value v = operand.getAliasedValue();
+ if (v.isPhi()) {
chainedPhis.add(operand.asPhi());
} else {
- if (operand != thisValue) {
+ if (v != thisValue) {
return false;
}
}
@@ -360,7 +361,7 @@
// Fixup `this` usages: rewrites all method calls so that they point to static methods.
private void fixupStaticizedThisUsers(IRCode code, Value thisValue) {
- assert thisValue != null;
+ assert thisValue != null && thisValue == thisValue.getAliasedValue();
// Depending on other optimizations, e.g., inlining, `this` can be flown to phis.
Set<Phi> trivialPhis = Sets.newIdentityHashSet();
boolean onlyHasTrivialPhis = testAndCollectPhisComposedOfThis(
@@ -368,10 +369,10 @@
assert thisValue.numberOfPhiUsers() == 0 || onlyHasTrivialPhis;
assert trivialPhis.isEmpty() || onlyHasTrivialPhis;
- Set<Instruction> users = SetUtils.newIdentityHashSet(thisValue.uniqueUsers());
+ Set<Instruction> users = SetUtils.newIdentityHashSet(thisValue.aliasedUsers());
// If that is the case, method calls we want to fix up include users of those phis.
for (Phi phi : trivialPhis) {
- users.addAll(phi.uniqueUsers());
+ users.addAll(phi.aliasedUsers());
}
fixupStaticizedValueUsers(code, users);
@@ -425,13 +426,14 @@
}
Set<Phi> chainedPhis = Sets.newIdentityHashSet();
for (Value operand : phi.getOperands()) {
- if (operand.isPhi()) {
+ Value v = operand.getAliasedValue();
+ if (v.isPhi()) {
chainedPhis.add(operand.asPhi());
} else {
- if (!operand.definition.isStaticGet()) {
+ if (!v.definition.isStaticGet()) {
return false;
}
- if (operand.definition.asStaticGet().getField() != field) {
+ if (v.definition.asStaticGet().getField() != field) {
return false;
}
}
@@ -458,10 +460,10 @@
assert dest.numberOfPhiUsers() == 0 || onlyHasTrivialPhis;
assert trivialPhis.isEmpty() || onlyHasTrivialPhis;
- Set<Instruction> users = SetUtils.newIdentityHashSet(dest.uniqueUsers());
+ Set<Instruction> users = SetUtils.newIdentityHashSet(dest.aliasedUsers());
// If that is the case, method calls we want to fix up include users of those phis.
for (Phi phi : trivialPhis) {
- users.addAll(phi.uniqueUsers());
+ users.addAll(phi.aliasedUsers());
}
fixupStaticizedValueUsers(code, users);
@@ -475,6 +477,9 @@
private void fixupStaticizedValueUsers(IRCode code, Set<Instruction> users) {
for (Instruction user : users) {
+ if (user.isAssume()) {
+ continue;
+ }
assert user.isInvokeVirtual() || user.isInvokeDirect();
InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
Value newValue = null;
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 8b7004e..18f8459 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -157,7 +157,7 @@
field.type.toSourceString());
}
- DexField toDexField(DexItemFactory factory, DexType clazz) {
+ public DexField toDexField(DexItemFactory factory, DexType clazz) {
return factory.createField(
clazz,
factory.createType(javaTypeToDescriptor(type)),
@@ -266,7 +266,7 @@
return name.substring(0, name.lastIndexOf(JAVA_PACKAGE_SEPARATOR));
}
- DexMethod toDexMethod(DexItemFactory factory, DexType clazz) {
+ public DexMethod toDexMethod(DexItemFactory factory, DexType clazz) {
DexType[] paramTypes = new DexType[parameters.length];
for (int i = 0; i < parameters.length; i++) {
paramTypes[i] = factory.createType(javaTypeToDescriptor(parameters[i]));
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 04d7501..bde48e7 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.utils.StringUtils.EMPTY_CHAR_ARRAY;
+import static com.android.tools.r8.utils.SymbolGenerationUtils.PRIMITIVE_TYPE_NAMES;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
@@ -106,17 +107,19 @@
String nextName(char[] packagePrefix, InternalNamingState state, boolean isDirectMethodCall) {
StringBuilder nextName = new StringBuilder();
nextName.append(packagePrefix);
- if (state.getDictionaryIndex() < obfuscationDictionary.size()) {
- nextName.append(obfuscationDictionary.get(state.incrementDictionaryIndex()));
- } else {
- String nextString;
- do {
- nextString =
- SymbolGenerationUtils.numberToIdentifier(
- state.incrementNameIndex(isDirectMethodCall), mixedCasing);
- } while (obfuscationDictionaryForLookup.contains(nextString));
- nextName.append(nextString);
- }
+ String nextString;
+ do {
+ if (state.getDictionaryIndex() < obfuscationDictionary.size()) {
+ nextString = obfuscationDictionary.get(state.incrementDictionaryIndex());
+ } else {
+ do {
+ nextString =
+ SymbolGenerationUtils.numberToIdentifier(
+ state.incrementNameIndex(isDirectMethodCall), mixedCasing);
+ } while (obfuscationDictionaryForLookup.contains(nextString));
+ }
+ } while (PRIMITIVE_TYPE_NAMES.contains(nextString));
+ nextName.append(nextString);
return nextName.toString();
}
}
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 1eb64de..2e7ed67 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -593,11 +593,13 @@
private class UseRegistry extends com.android.tools.r8.graph.UseRegistry {
+ private final DexProgramClass currentHolder;
private final DexEncodedMethod currentMethod;
private UseRegistry(DexItemFactory factory, DexProgramClass holder, DexEncodedMethod method) {
super(factory);
assert holder.type == method.method.holder;
+ this.currentHolder = holder;
this.currentMethod = method;
}
@@ -607,7 +609,7 @@
@Override
public boolean registerInvokeVirtual(DexMethod method) {
- return registerInvokeVirtual(method, KeepReason.invokedFrom(currentMethod));
+ return registerInvokeVirtual(method, KeepReason.invokedFrom(currentHolder, currentMethod));
}
boolean registerInvokeVirtual(DexMethod method, KeepReason keepReason) {
@@ -632,7 +634,7 @@
@Override
public boolean registerInvokeDirect(DexMethod method) {
- return registerInvokeDirect(method, KeepReason.invokedFrom(currentMethod));
+ return registerInvokeDirect(method, KeepReason.invokedFrom(currentHolder, currentMethod));
}
boolean registerInvokeDirect(DexMethod method, KeepReason keepReason) {
@@ -648,7 +650,7 @@
@Override
public boolean registerInvokeStatic(DexMethod method) {
- return registerInvokeStatic(method, KeepReason.invokedFrom(currentMethod));
+ return registerInvokeStatic(method, KeepReason.invokedFrom(currentHolder, currentMethod));
}
boolean registerInvokeStatic(DexMethod method, KeepReason keepReason) {
@@ -683,7 +685,7 @@
@Override
public boolean registerInvokeInterface(DexMethod method) {
- return registerInvokeInterface(method, KeepReason.invokedFrom(currentMethod));
+ return registerInvokeInterface(method, KeepReason.invokedFrom(currentHolder, currentMethod));
}
boolean registerInvokeInterface(DexMethod method, KeepReason keepReason) {
@@ -2028,7 +2030,7 @@
.createMethod(enumClass.type, proto, appView.dexItemFactory().createString("values"));
}
- private void markEnumValuesAsReachable(DexClass clazz, KeepReason reason) {
+ private void markEnumValuesAsReachable(DexProgramClass clazz, KeepReason reason) {
DexEncodedMethod valuesMethod = clazz.lookupMethod(generatedEnumValuesMethod(clazz));
if (valuesMethod != null) {
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
@@ -2847,10 +2849,11 @@
// SomeEnumClass.valueOf(java.lang.String) which is generated by javac for all enums will
// call this method.
if (invoke.inValues().get(0).isConstClass()) {
- DexClass clazz =
- appView.definitionFor(invoke.inValues().get(0).definition.asConstClass().getValue());
- if (clazz.accessFlags.isEnum() && clazz.superType == appView.dexItemFactory().enumType) {
- markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(method));
+ DexType type = invoke.inValues().get(0).definition.asConstClass().getValue();
+ DexProgramClass clazz = getProgramClassOrNull(type);
+ if (clazz != null && clazz.accessFlags.isEnum()) {
+ DexProgramClass holder = getProgramClassOrNull(method.method.holder);
+ markEnumValuesAsReachable(clazz, KeepReason.invokedFrom(holder, method));
}
}
}
@@ -3023,8 +3026,8 @@
// will use the values() method on that enum class.
if (options.isGeneratingClassFiles()
&& annotationHolder == dexItemFactory.annotationDefault) {
- DexClass clazz = appView.definitionFor(field.type);
- if (clazz != null && clazz.isProgramClass() && clazz.accessFlags.isEnum()) {
+ DexProgramClass clazz = getProgramClassOrNull(field.type);
+ if (clazz != null && clazz.accessFlags.isEnum()) {
markEnumValuesAsReachable(clazz, KeepReason.referencedInAnnotation(annotationHolder));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index a59f9e1..eae4a9b 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -43,8 +43,8 @@
return new ReachableFromLiveType(type);
}
- public static KeepReason invokedFrom(DexEncodedMethod method) {
- return new InvokedFrom(method);
+ public static KeepReason invokedFrom(DexProgramClass holder, DexEncodedMethod method) {
+ return new InvokedFrom(holder, method);
}
public static KeepReason invokedFromLambdaCreatedIn(DexEncodedMethod method) {
@@ -252,8 +252,9 @@
private static class InvokedFrom extends BasedOnOtherMethod {
- private InvokedFrom(DexEncodedMethod method) {
+ private InvokedFrom(DexProgramClass holder, DexEncodedMethod method) {
super(method);
+ assert holder.type == method.method.holder;
}
@Override
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 9c135c8..d78cb45 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -211,8 +211,7 @@
public boolean enableNameReflectionOptimization = true;
public boolean enableStringConcatenationOptimization = true;
public boolean enableTreeShakingOfLibraryMethodOverrides = false;
- // TODO(b/139246447): enable after branching.
- public boolean enableCallSiteOptimizationInfoPropagation = false;
+ public boolean enableCallSiteOptimizationInfoPropagation = true;
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
@@ -354,7 +353,12 @@
}
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
- featureSplit.getProgramConsumer().finished(reporter);
+ ProgramConsumer programConsumer = featureSplit.getProgramConsumer();
+ programConsumer.finished(reporter);
+ DataResourceConsumer dataResourceConsumer = programConsumer.getDataResourceConsumer();
+ if (dataResourceConsumer != null) {
+ dataResourceConsumer.finished(reporter);
+ }
}
}
}
@@ -1015,13 +1019,6 @@
enableNameReflectionOptimization = false;
}
- // TODO(b/139246447): Remove this once enabled.
- @VisibleForTesting
- public void enableCallSiteOptimizationInfoPropagation() {
- assert !enableCallSiteOptimizationInfoPropagation;
- enableCallSiteOptimizationInfoPropagation = true;
- }
-
private boolean hasMinApi(AndroidApiLevel level) {
assert isGeneratingDex();
return minApiLevel >= level.getLevel();
diff --git a/src/main/java/com/android/tools/r8/utils/SymbolGenerationUtils.java b/src/main/java/com/android/tools/r8/utils/SymbolGenerationUtils.java
index b66ca5d..9e23205 100644
--- a/src/main/java/com/android/tools/r8/utils/SymbolGenerationUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SymbolGenerationUtils.java
@@ -6,7 +6,9 @@
import static com.android.tools.r8.utils.StringUtils.EMPTY_CHAR_ARRAY;
+import com.google.common.collect.Sets;
import java.util.Arrays;
+import java.util.Set;
public class SymbolGenerationUtils {
@@ -15,6 +17,9 @@
DONT_USE_MIXED_CASE
}
+ public static Set<String> PRIMITIVE_TYPE_NAMES =
+ Sets.newHashSet("boolean", "byte", "char", "double", "float", "int", "long", "short", "void");
+
// These letters are used not creating fresh names to output and not for parsing dex/class files.
private static final char[] IDENTIFIER_CHARACTERS =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
diff --git a/src/test/desugaredLibraryConversions/stubs/Duration.java b/src/test/desugaredLibraryConversions/stubs/Duration.java
index e559a8f..e49693f 100644
--- a/src/test/desugaredLibraryConversions/stubs/Duration.java
+++ b/src/test/desugaredLibraryConversions/stubs/Duration.java
@@ -9,7 +9,7 @@
return null;
}
- public int getSeconds() {
+ public long getSeconds() {
return 0;
}
diff --git a/src/test/examplesJava9/backport/MapBackportJava9Main.java b/src/test/examplesJava9/backport/MapBackportJava9Main.java
index 2aa5cfd..442fd9b 100644
--- a/src/test/examplesJava9/backport/MapBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/MapBackportJava9Main.java
@@ -15,6 +15,7 @@
testOf2();
testOf10();
testOfEntries();
+ testEntry();
}
private static void testOf0() {
@@ -218,6 +219,31 @@
}
}
+ private static void testEntry() {
+ Object key = new Object();
+ Object value = new Object();
+ Map.Entry<Object, Object> entry = Map.entry(key, value);
+ assertSame(key, entry.getKey());
+ assertSame(value, entry.getValue());
+
+ try {
+ entry.setValue(new Object());
+ throw new AssertionError();
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ try {
+ Map.entry(null, value);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Map.entry(key, null);
+ throw new AssertionError();
+ } catch (NullPointerException expected) {
+ }
+ }
+
private static void assertMutationNotAllowed(Map<Object, Object> ofObject) {
try {
ofObject.put(new Object(), new Object());
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2819cc7..4256f09 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -162,6 +162,10 @@
public static final Path DESUGAR_LIB_JSON_FOR_TESTING =
Paths.get("src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json");
+ public static boolean isLocalDevelopment() {
+ return System.getProperty("local_development", "0").equals("1");
+ }
+
public static boolean shouldRunSlowTests() {
return System.getProperty("slow_tests", "0").equals("1");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java
deleted file mode 100644
index 0377278..0000000
--- a/src/test/java/com/android/tools/r8/desugar/corelib/JsonCompatibilityTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.desugar.corelib;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.DesugaredLibraryConfigurationForTesting;
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import java.nio.file.Paths;
-import java.util.Map;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class JsonCompatibilityTest extends TestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
- }
-
- public JsonCompatibilityTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testCompatibilityProgram() {
- DexItemFactory factory = new DexItemFactory();
- InternalOptions options1 = new InternalOptions(factory, new Reporter());
- options1.minApiLevel = parameters.getApiLevel().getLevel();
- options1.desugaredLibraryConfiguration =
- DesugaredLibraryConfigurationForTesting.configureLibraryDesugaringForProgramCompilation(
- parameters.getApiLevel().getLevel(), factory);
-
- Reporter reporter = new Reporter();
- InternalOptions options2 = new InternalOptions(factory, reporter);
- options2.minApiLevel = parameters.getApiLevel().getLevel();
- options2.desugaredLibraryConfiguration =
- new DesugaredLibraryConfigurationParser(
- factory, reporter, false, parameters.getApiLevel().getLevel())
- .parse(
- StringResource.fromFile(
- Paths.get(
- "src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json")));
-
- assertConfigurationEquals(
- options1.desugaredLibraryConfiguration, options2.desugaredLibraryConfiguration);
- }
-
- @Test
- public void testCompatibilityLibrary() {
- DexItemFactory factory = new DexItemFactory();
- InternalOptions options1 = new InternalOptions(factory, new Reporter());
- options1.minApiLevel = parameters.getApiLevel().getLevel();
- options1.desugaredLibraryConfiguration =
- DesugaredLibraryConfigurationForTesting.configureLibraryDesugaringForLibraryCompilation(
- parameters.getApiLevel().getLevel(), factory);
-
- Reporter reporter = new Reporter();
- InternalOptions options2 = new InternalOptions(factory, reporter);
- options2.minApiLevel = parameters.getApiLevel().getLevel();
- options2.desugaredLibraryConfiguration =
- new DesugaredLibraryConfigurationParser(
- factory, reporter, true, parameters.getApiLevel().getLevel())
- .parse(
- StringResource.fromFile(
- Paths.get(
- "src/test/java/com/android/tools/r8/desugar/corelib/desugar_jdk_libs.json")));
-
- assertConfigurationEquals(
- options1.desugaredLibraryConfiguration, options2.desugaredLibraryConfiguration);
- }
-
- private void assertConfigurationEquals(
- DesugaredLibraryConfiguration libraryConfiguration1,
- DesugaredLibraryConfiguration libraryConfiguration2) {
- assertDictEquals(
- libraryConfiguration1.getRewritePrefix(), libraryConfiguration2.getRewritePrefix());
- assertDictEquals(
- libraryConfiguration1.getEmulateLibraryInterface(),
- libraryConfiguration2.getEmulateLibraryInterface());
- assertDictEquals(
- libraryConfiguration1.getBackportCoreLibraryMember(),
- libraryConfiguration2.getBackportCoreLibraryMember());
- assertRetargetEquals(
- libraryConfiguration1.getRetargetCoreLibMember(),
- libraryConfiguration2.getRetargetCoreLibMember());
- assertEquals(
- libraryConfiguration1.getDontRewriteInvocation().size(),
- libraryConfiguration1.getDontRewriteInvocation().size());
- }
-
- private void assertRetargetEquals(
- Map<DexString, Map<DexType, DexType>> retarget1,
- Map<DexString, Map<DexType, DexType>> retarget2) {
- assertEquals(retarget1.size(), retarget2.size());
- for (DexString dexString : retarget1.keySet()) {
- assert retarget2.containsKey(dexString);
- assertDictEquals(retarget1.get(dexString), retarget2.get(dexString));
- }
- }
-
- private <E> void assertDictEquals(Map<E, E> map1, Map<E, E> map2) {
- assertEquals(map1.size(), map2.size());
- for (E key : map1.keySet()) {
- assertTrue(map2.containsKey(key) && map1.get(key).equals(map2.get(key)));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
index eca2a47..e97834f 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/APIConversionTestBase.java
@@ -14,12 +14,17 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
+import org.junit.Assume;
public class APIConversionTestBase extends CoreLibDesugarTestBase {
private static final Path CONVERSION_FOLDER = Paths.get("src/test/desugaredLibraryConversions");
public Path[] getTimeConversionClasses() throws IOException {
+ Assume.assumeTrue(
+ "JDK8 javac is required to avoid dealing with modules and JDK8 is not checked-in on"
+ + " windows",
+ !ToolHelper.isWindows());
File conversionFolder = temp.newFolder("conversions");
File stubsFolder = temp.newFolder("stubs");
@@ -28,7 +33,7 @@
CfVm.JDK8,
null,
stubsFolder.toPath(),
- getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs/"), "java"));
+ getAllFilesWithSuffixInDirectory(CONVERSION_FOLDER.resolve("stubs"), "java"));
// Compile the conversions using the stubs.
ArrayList<Path> classPath = new ArrayList<>();
@@ -46,7 +51,7 @@
}
protected Path buildDesugaredLibraryWithConversionExtension(AndroidApiLevel apiLevel) {
- Path[] timeConversionClasses = null;
+ Path[] timeConversionClasses;
try {
timeConversionClasses = getTimeConversionClasses();
} catch (IOException e) {
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/AllTimeConversionTest.java
new file mode 100644
index 0000000..71e1a3f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/AllTimeConversionTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.corelib.conversionTests;
+
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.MonthDay;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import org.junit.Test;
+
+public class AllTimeConversionTest extends APIConversionTestBase {
+
+ @Test
+ public void testRewrittenAPICalls() throws Exception {
+ Path customLib = testForD8().addProgramClasses(CustomLibClass.class).compile().writeToZip();
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibraryWithConversionExtension, AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
+ .assertSuccessWithOutput(
+ StringUtils.lines(
+ "1970-01-02T00:00Z[GMT]",
+ "PT0.000012345S",
+ "GMT",
+ "--03-02",
+ "-1000000000-01-01T00:00:00.999999999Z",
+ "GMT",
+ "GMT"));
+ }
+
+ static class Executor {
+
+ private static final String ZONE_ID = "GMT";
+
+ public static void main(String[] args) {
+ returnValueUsed();
+ returnValueUnused();
+ virtualMethods();
+ }
+
+ public static void returnValueUsed() {
+ System.out.println(
+ CustomLibClass.mix(
+ ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneId.of(ZONE_ID)),
+ ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneId.of(ZONE_ID))));
+ CustomLibClass.mix(LocalDate.of(2000, 3, 13), LocalDate.of(1990, 5, 25));
+ System.out.println(CustomLibClass.mix(Duration.ZERO, Duration.ofNanos(12345)));
+ System.out.println(CustomLibClass.mix(ZoneId.of(ZONE_ID), ZoneId.of(ZONE_ID)));
+ System.out.println(CustomLibClass.mix(MonthDay.of(3, 4), MonthDay.of(1, 2)));
+ System.out.println(CustomLibClass.mix(Instant.MIN, Instant.MAX));
+ }
+
+ public static void returnValueUnused() {
+ CustomLibClass.mix(
+ ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneId.of(ZONE_ID)),
+ ZonedDateTime.ofInstant(Instant.ofEpochSecond(0), ZoneId.of(ZONE_ID)));
+ CustomLibClass.mix(LocalDate.of(2000, 3, 13), LocalDate.of(1990, 5, 25));
+ CustomLibClass.mix(Duration.ZERO, Duration.ofNanos(12345));
+ CustomLibClass.mix(ZoneId.of(ZONE_ID), ZoneId.of(ZONE_ID));
+ CustomLibClass.mix(MonthDay.of(3, 4), MonthDay.of(1, 2));
+ CustomLibClass.mix(Instant.MIN, Instant.MAX);
+ }
+
+ public static void virtualMethods() {
+ ZoneId of = ZoneId.of(ZONE_ID);
+ CustomLibClass customLibClass = new CustomLibClass();
+ customLibClass.virtual(of);
+ customLibClass.virtualString(of);
+ System.out.println(customLibClass.virtual(of));
+ System.out.println(customLibClass.virtualString(of));
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. None of the methods make sense.
+ static class CustomLibClass {
+
+ public static ZonedDateTime mix(ZonedDateTime zonedDateTime1, ZonedDateTime zonedDateTime2) {
+ return zonedDateTime1.plusDays(zonedDateTime2.getDayOfMonth());
+ }
+
+ public static LocalDate mix(LocalDate localDate1, LocalDate localDate2) {
+ return localDate1.plusDays(localDate2.getDayOfYear());
+ }
+
+ public static Duration mix(Duration duration1, Duration duration2) {
+ return duration1.plus(duration2);
+ }
+
+ public static ZoneId mix(ZoneId zoneId1, ZoneId zoneId2) {
+ return zoneId1;
+ }
+
+ public static MonthDay mix(MonthDay monthDay1, MonthDay monthDay2) {
+ return monthDay1.withDayOfMonth(monthDay2.getDayOfMonth());
+ }
+
+ public static Instant mix(Instant instant1, Instant instant2) {
+ return instant1.plusNanos(instant2.getNano());
+ }
+
+ public ZoneId virtual(ZoneId zoneId) {
+ return zoneId;
+ }
+
+ public String virtualString(ZoneId zoneId) {
+ return zoneId.getId();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/BasicTimeConversionTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
rename to src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/BasicTimeConversionTest.java
index 2cdf6e7..e82ae21 100644
--- a/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/TimeConversionCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/corelib/conversionTests/BasicTimeConversionTest.java
@@ -25,7 +25,7 @@
import java.util.TimeZone;
import org.junit.Test;
-public class TimeConversionCompilationTest extends APIConversionTestBase {
+public class BasicTimeConversionTest extends APIConversionTestBase {
@Test
public void testTimeGeneratedDex() throws Exception {
@@ -53,7 +53,7 @@
public void testRewrittenAPICalls() throws Exception {
testForD8()
.setMinApi(AndroidApiLevel.B)
- .addInnerClasses(TimeConversionCompilationTest.class)
+ .addInnerClasses(BasicTimeConversionTest.class)
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compile()
.inspect(this::checkAPIRewritten)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
index 5622177..37b03d6 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8FeatureSplitTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.dexsplitter;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DexIndexedConsumer;
@@ -18,10 +20,13 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,7 +50,7 @@
private static FeatureSplit emptySplitProvider(FeatureSplit.Builder builder) {
builder
- .addProgramResourceProvider(() -> ImmutableList.of())
+ .addProgramResourceProvider(ImmutableList::of)
.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
return builder.build();
}
@@ -111,6 +116,74 @@
assertEquals(markers.iterator().next(), feature2Markers.iterator().next());
}
+ @Test
+ public void testNonJavaPassThrough() throws IOException, CompilationFailedException {
+ Path basePath = temp.newFile("base.zip").toPath();
+ Path feature1Path = temp.newFile("feature1.zip").toPath();
+ Path feature2Path = temp.newFile("feature2.zip").toPath();
+ Collection<String> nonJavaFiles = ImmutableList.of("foobar", "barfoo");
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+ .setMinApi(parameters.getRuntime())
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder, feature1Path, temp, nonJavaFiles, true, FeatureClass.class))
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder, feature2Path, temp, nonJavaFiles, true, FeatureClass2.class))
+ .addKeepAllClassesRule()
+ .compile()
+ .writeToZip(basePath);
+ for (Path feature : ImmutableList.of(feature1Path, feature2Path)) {
+ ZipFile zipFile = new ZipFile(feature.toFile());
+ for (String nonJavaFile : nonJavaFiles) {
+ ZipEntry entry = zipFile.getEntry(nonJavaFile);
+ assertNotNull(entry);
+ String content = new String(ByteStreams.toByteArray(zipFile.getInputStream(entry)));
+ assertEquals(content, nonJavaFile);
+ }
+ }
+ }
+
+ @Test
+ public void testAdaptResourceContentInSplits() throws IOException, CompilationFailedException {
+ Path basePath = temp.newFile("base.zip").toPath();
+ Path feature1Path = temp.newFile("feature1.zip").toPath();
+ Path feature2Path = temp.newFile("feature2.zip").toPath();
+ // Make the content of the data resource be class names
+ Collection<String> nonJavaFiles =
+ ImmutableList.of(FeatureClass.class.getName(), FeatureClass2.class.getName());
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(BaseClass.class, RunInterface.class, SplitRunner.class)
+ .setMinApi(parameters.getRuntime())
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder, feature1Path, temp, nonJavaFiles, false, FeatureClass.class))
+ .addFeatureSplit(
+ builder ->
+ splitWithNonJavaFile(
+ builder, feature2Path, temp, nonJavaFiles, false, FeatureClass2.class))
+ .addKeepClassRulesWithAllowObfuscation(
+ BaseClass.class, FeatureClass.class, FeatureClass2.class)
+ .addKeepRules("-adaptresourcefilecontents")
+ .compile()
+ .writeToZip(basePath);
+ for (Path feature : ImmutableList.of(feature1Path, feature2Path)) {
+ ZipFile zipFile = new ZipFile(feature.toFile());
+ for (String nonJavaFile : nonJavaFiles) {
+ ZipEntry entry = zipFile.getEntry(nonJavaFile);
+ assertNotNull(entry);
+ String content = new String(ByteStreams.toByteArray(zipFile.getInputStream(entry)));
+ assertNotEquals(content, nonJavaFile);
+ }
+ }
+ }
+
public static class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
@@ -169,6 +242,7 @@
return feature2Path;
}
+
public CompiledWithFeature invoke() throws IOException, CompilationFailedException {
basePath = temp.newFile("base.zip").toPath();
feature1Path = temp.newFile("feature1.zip").toPath();
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index b8154cc..fea2bce 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -3,7 +3,6 @@
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ArchiveProgramResourceProvider;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
@@ -18,11 +17,15 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.dexsplitter.DexSplitter.Options;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
import dalvik.system.PathClassLoader;
import java.io.IOException;
import java.net.MalformedURLException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
@@ -31,6 +34,9 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
import org.junit.rules.TemporaryFolder;
public class SplitterTestBase extends TestBase {
@@ -45,32 +51,78 @@
Path outputPath,
TemporaryFolder temp,
Collection<Class<?>> classes) {
+ addConsumers(builder, outputPath, temp, null, true, classes);
+ return builder.build();
+ }
+
+ private static void addConsumers(
+ FeatureSplit.Builder builder,
+ Path outputPath,
+ TemporaryFolder temp,
+ Collection<String> nonJavaResources,
+ boolean ensureClassesInOutput,
+ Collection<Class<?>> classes) {
List<String> classNames = classes.stream().map(Class::getName).collect(Collectors.toList());
Path featureJar;
try {
- featureJar = temp.newFile().toPath();
+ featureJar = temp.newFolder().toPath().resolve("feature.jar");
writeClassesToJar(featureJar, classes);
+ if (nonJavaResources != null && nonJavaResources.size() > 0) {
+ // We can't simply append to an existing zip easily, just copy the entries and add what we
+ // need.
+ Path newFeatureJar = temp.newFolder().toPath().resolve("feature.jar");
+
+ ZipOutputStream outputStream = new ZipOutputStream(Files.newOutputStream(newFeatureJar));
+ ZipInputStream inputStream = new ZipInputStream(Files.newInputStream(featureJar));
+ ZipEntry next = inputStream.getNextEntry();
+ while (next != null) {
+ outputStream.putNextEntry(new ZipEntry(next.getName()));
+ outputStream.write(ByteStreams.toByteArray(inputStream));
+ outputStream.closeEntry();
+ next = inputStream.getNextEntry();
+ }
+
+ for (String nonJavaResource : nonJavaResources) {
+ ZipUtils.writeToZipStream(
+ outputStream, nonJavaResource, nonJavaResource.getBytes(), ZipEntry.STORED);
+ }
+ outputStream.close();
+ featureJar = newFeatureJar;
+ }
} catch (IOException e) {
assertTrue(false);
- return null;
+ return;
}
builder
- .addProgramResourceProvider(ArchiveProgramResourceProvider.fromArchive(featureJar))
+ .addProgramResourceProvider(ArchiveResourceProvider.fromArchive(featureJar, true))
.setProgramConsumer(
- new ArchiveConsumer(outputPath) {
+ new ArchiveConsumer(outputPath, true) {
@Override
public void accept(
int fileIndex,
ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
- for (String descriptor : descriptors) {
- assertTrue(classNames.contains(DescriptorUtils.descriptorToJavaType(descriptor)));
+ if (ensureClassesInOutput) {
+ for (String descriptor : descriptors) {
+ assertTrue(
+ classNames.contains(DescriptorUtils.descriptorToJavaType(descriptor)));
+ }
}
super.accept(fileIndex, data, descriptors, handler);
}
});
+ }
+
+ protected static FeatureSplit splitWithNonJavaFile(
+ FeatureSplit.Builder builder,
+ Path outputPath,
+ TemporaryFolder temp,
+ Collection<String> nonJavaFiles,
+ boolean ensureClassesInOutput,
+ Class... classes) {
+ addConsumers(builder, outputPath, temp, nonJavaFiles, true, Arrays.asList(classes));
return builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
index 15d2803..4ffbe2b 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
@@ -47,6 +47,14 @@
return result;
}
+ public Path getReleaseApk() {
+ return Paths.get(base).resolve("YouTubeRelease.apk");
+ }
+
+ public Path getReleaseProguardMap() {
+ return Paths.get(base).resolve("YouTubeRelease_proguard.map");
+ }
+
void runR8AndCheckVerification(CompilationMode mode, String input) throws Exception {
runAndCheckVerification(
CompilerUnderTest.R8, mode, base + APK, null, null, ImmutableList.of(base + input));
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
similarity index 90%
rename from src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
rename to src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
index 8856521..09cfa48 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1217TreeShakeJarVerificationTest.java
@@ -14,9 +14,9 @@
import java.nio.file.Path;
import org.junit.Test;
-public class YouTubeTreeShakeJarVerificationTest extends YouTubeCompilationBase {
+public class YouTubeV1217TreeShakeJarVerificationTest extends YouTubeCompilationBase {
- public YouTubeTreeShakeJarVerificationTest() {
+ public YouTubeV1217TreeShakeJarVerificationTest() {
super(12, 17);
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..95004d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
+import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+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.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.analysis.ProtoApplicationStats;
+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 YouTubeV1419TreeShakeJarVerificationTest extends YouTubeCompilationBase {
+
+ private static final int MAX_SIZE = 27500000;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().build();
+ }
+
+ public YouTubeV1419TreeShakeJarVerificationTest(TestParameters parameters) {
+ super(14, 19);
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/141603168): Enable this on the bots.
+ assumeTrue(isLocalDevelopment());
+ assumeTrue(shouldRunSlowTests());
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addKeepRuleFiles(getKeepRuleFiles())
+ .addOptionsModification(
+ options -> {
+ assert !options.enableFieldBitAccessAnalysis;
+ options.enableFieldBitAccessAnalysis = true;
+
+ assert !options.enableGeneratedExtensionRegistryShrinking;
+ options.enableGeneratedExtensionRegistryShrinking = true;
+
+ assert !options.enableGeneratedMessageLiteShrinking;
+ options.enableGeneratedMessageLiteShrinking = true;
+
+ assert !options.enableStringSwitchConversion;
+ options.enableStringSwitchConversion = true;
+ })
+ .allowUnusedProguardConfigurationRules()
+ .compile();
+
+ if (ToolHelper.isLocalDevelopment()) {
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProtoApplicationStats original =
+ new ProtoApplicationStats(dexItemFactory, new CodeInspector(getProgramFiles()));
+ ProtoApplicationStats actual =
+ new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
+ ProtoApplicationStats baseline =
+ new ProtoApplicationStats(
+ dexItemFactory,
+ new CodeInspector(getReleaseApk(), getReleaseProguardMap().toString()));
+ System.out.println(actual.getStats(baseline));
+ }
+
+ int applicationSize = applicationSize(compileResult.app);
+ System.out.println(applicationSize);
+
+ assertTrue(
+ "Expected max size of " + MAX_SIZE + ", got " + applicationSize,
+ applicationSize < MAX_SIZE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 69dc44b..59e114e 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -9,10 +9,14 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
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.analysis.ProtoApplicationStats;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.List;
@@ -68,65 +72,74 @@
@Test
public void test() throws Exception {
CodeInspector inputInspector = new CodeInspector(PROGRAM_FILES);
- testForR8(parameters.getBackend())
- .addProgramFiles(PROGRAM_FILES)
- .addKeepMainRule("proto2.TestClass")
- .addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
- .addKeepRules(alwaysInlineNewSingularGeneratedExtensionRule())
- // TODO(b/112437944): Attempt to prove that DEFAULT_INSTANCE is non-null, such that the
- // following "assumenotnull" rule can be omitted.
- .addKeepRules(
- "-assumenosideeffects class " + EXT_C + " {",
- " private static final " + EXT_C + " DEFAULT_INSTANCE return 1..42;",
- "}")
- .addOptionsModification(
- options -> {
- options.enableFieldBitAccessAnalysis = true;
- options.enableGeneratedMessageLiteShrinking = true;
- options.enableGeneratedExtensionRegistryShrinking = true;
- options.enableStringSwitchConversion = true;
- })
- .allowAccessModification(allowAccessModification)
- .allowUnusedProguardConfigurationRules()
- .minification(enableMinification)
- .setMinApi(parameters.getRuntime())
- .compile()
- .inspect(
- outputInspector -> {
- verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
- verifyUnusedExtensionsAreRemoved(inputInspector, outputInspector);
- verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
- verifyUnusedHazzerBitFieldsAreRemoved(inputInspector, outputInspector);
- verifyUnusedTypesAreRemoved(inputInspector, outputInspector);
- })
- .run(parameters.getRuntime(), "proto2.TestClass")
- .assertSuccessWithOutputLines(
- "--- roundtrip ---",
- "true",
- "123",
- "asdf",
- "9223372036854775807",
- "qwerty",
- "--- partiallyUsed_proto2 ---",
- "true",
- "42",
- "--- usedViaHazzer ---",
- "true",
- "--- usedViaOneofCase ---",
- "true",
- "--- usesOnlyRepeatedFields ---",
- "1",
- "--- containsFlaggedOffField ---",
- "0",
- "--- hasFlaggedOffExtension ---",
- "4",
- "--- useOneExtension ---",
- "42",
- "--- keepMapAndRequiredFields ---",
- "true",
- "10",
- "10",
- "10");
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(PROGRAM_FILES)
+ .addKeepMainRule("proto2.TestClass")
+ .addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
+ .addKeepRules(alwaysInlineNewSingularGeneratedExtensionRule())
+ // TODO(b/112437944): Attempt to prove that DEFAULT_INSTANCE is non-null, such that the
+ // following "assumenotnull" rule can be omitted.
+ .addKeepRules(
+ "-assumenosideeffects class " + EXT_C + " {",
+ " private static final " + EXT_C + " DEFAULT_INSTANCE return 1..42;",
+ "}")
+ .addOptionsModification(
+ options -> {
+ options.enableFieldBitAccessAnalysis = true;
+ options.enableGeneratedMessageLiteShrinking = true;
+ options.enableGeneratedExtensionRegistryShrinking = true;
+ options.enableStringSwitchConversion = true;
+ })
+ .allowAccessModification(allowAccessModification)
+ .allowUnusedProguardConfigurationRules()
+ .minification(enableMinification)
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(
+ outputInspector -> {
+ verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
+ verifyUnusedExtensionsAreRemoved(inputInspector, outputInspector);
+ verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
+ verifyUnusedHazzerBitFieldsAreRemoved(inputInspector, outputInspector);
+ verifyUnusedTypesAreRemoved(inputInspector, outputInspector);
+ })
+ .run(parameters.getRuntime(), "proto2.TestClass")
+ .assertSuccessWithOutputLines(
+ "--- roundtrip ---",
+ "true",
+ "123",
+ "asdf",
+ "9223372036854775807",
+ "qwerty",
+ "--- partiallyUsed_proto2 ---",
+ "true",
+ "42",
+ "--- usedViaHazzer ---",
+ "true",
+ "--- usedViaOneofCase ---",
+ "true",
+ "--- usesOnlyRepeatedFields ---",
+ "1",
+ "--- containsFlaggedOffField ---",
+ "0",
+ "--- hasFlaggedOffExtension ---",
+ "4",
+ "--- useOneExtension ---",
+ "42",
+ "--- keepMapAndRequiredFields ---",
+ "true",
+ "10",
+ "10",
+ "10");
+
+ if (ToolHelper.isLocalDevelopment()) {
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProtoApplicationStats original = new ProtoApplicationStats(dexItemFactory, inputInspector);
+ ProtoApplicationStats actual =
+ new ProtoApplicationStats(dexItemFactory, result.inspector(), original);
+ System.out.println(actual.getStats());
+ }
}
private void verifyMapAndRequiredFieldsAreKept(
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
index 0dd1a53..ae3aef1 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CollectionMethods.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar.backports;
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -44,4 +45,10 @@
}
return Collections.unmodifiableMap(map);
}
+
+ public static <K, V> Map.Entry<K, V> mapEntry(K key, V value) {
+ return new AbstractMap.SimpleImmutableEntry<>(
+ Objects.requireNonNull(key),
+ Objects.requireNonNull(value));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
index 0c77830..1890534 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -45,7 +44,6 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
index 0ee0af3..ce2fd2e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -45,7 +44,6 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.addOptionsModification(o -> {
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
index 09dd632..98012dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -43,7 +42,6 @@
.addKeepMainRule(MAIN)
.enableMergeAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
index 27057c1..cd4852d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -45,7 +44,6 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A:Sub1", "B:Sub1")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index 9eef5ac..1db3da7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -43,7 +42,6 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
index ae8ef12..b838d12 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -43,7 +42,6 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.addOptionsModification(o -> {
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index 53c9f8f..f1081fc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -42,7 +41,6 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.setMinApi(parameters.getRuntime())
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index bc4d0a7..7d0838c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -45,7 +44,6 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .addOptionsModification(InternalOptions::enableCallSiteOptimizationInfoPropagation)
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A", "B")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
index 79ea92e..d55484d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
@@ -44,7 +44,6 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.enableClassInliningAnnotations()
- .noMinification()
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Input")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java
new file mode 100644
index 0000000..a93e72b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionAsArgumentTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.staticizer;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.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 CompanionAsArgumentTest extends TestBase {
+ private static final Class<?> MAIN = Main.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // TODO(b/112831361): support for class staticizer in CF backend.
+ return getTestParameters().withDexRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public CompanionAsArgumentTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(CompanionAsArgumentTest.class)
+ .addKeepMainRule(MAIN)
+ .enableInliningAnnotations()
+ .enableClassInliningAnnotations()
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines("Companion#foo(true)")
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ // Check if the candidate is not staticized.
+ ClassSubject companion = inspector.clazz(Host.Companion.class);
+ assertThat(companion, isPresent());
+ MethodSubject foo = companion.uniqueMethodWithName("foo");
+ assertThat(foo, isPresent());
+ assertTrue(foo.streamInstructions().anyMatch(
+ i -> i.isInvokeVirtual()
+ && i.getMethod().toSourceString().contains("PrintStream.println")));
+
+ // Nothing migrated from Companion to Host.
+ ClassSubject host = inspector.clazz(Host.class);
+ assertThat(host, isPresent());
+ MethodSubject migrated_foo = host.uniqueMethodWithName("foo");
+ assertThat(migrated_foo, not(isPresent()));
+ }
+
+ @NeverClassInline
+ static class Host {
+ private static final Companion companion = new Companion();
+
+ static class Companion {
+ @NeverInline
+ public void foo(Object arg) {
+ System.out.println("Companion#foo(" + (arg != null) + ")");
+ }
+ }
+
+ @NeverInline
+ static void bar() {
+ // The target singleton is used as not only a receiver but also an argument.
+ companion.foo(companion);
+ }
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ Host.bar();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index a044d3f..59aae43 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -7,10 +7,8 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@@ -35,7 +33,6 @@
final String extraRules =
keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
runTest(FOLDER, mainClassName, extraRules,
- InternalOptions::enableCallSiteOptimizationInfoPropagation,
app -> {
CodeInspector codeInspector = new CodeInspector(app);
ClassSubject clazz = checkClassIsKept(codeInspector, ex1.getClassName());
@@ -61,13 +58,13 @@
final String extraRules =
keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
runTest(FOLDER, mainClassName, extraRules,
- InternalOptions::enableCallSiteOptimizationInfoPropagation,
app -> {
CodeInspector codeInspector = new CodeInspector(app);
ClassSubject clazz = checkClassIsKept(codeInspector, ex2.getClassName());
MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
- long ifzCount = testMethod.streamInstructions().filter(InstructionSubject::isIfEqz).count();
+ long ifzCount =
+ testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
long paramNullCheckCount =
countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
// ?: in aOrDefault
@@ -86,13 +83,13 @@
final String extraRules =
keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
runTest(FOLDER, mainClassName, extraRules,
- InternalOptions::enableCallSiteOptimizationInfoPropagation,
app -> {
CodeInspector codeInspector = new CodeInspector(app);
ClassSubject clazz = checkClassIsKept(codeInspector, ex3.getClassName());
MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
- long ifzCount = testMethod.streamInstructions().filter(InstructionSubject::isIfEqz).count();
+ long ifzCount =
+ testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
// !! operator inside explicit null check should be gone.
// One explicit null-check as well as 4 bar? accesses.
assertEquals(5, ifzCount);
diff --git a/src/test/java/com/android/tools/r8/naming/ObfuscationPrimitiveNamesTest.java b/src/test/java/com/android/tools/r8/naming/ObfuscationPrimitiveNamesTest.java
new file mode 100644
index 0000000..176cad1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ObfuscationPrimitiveNamesTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.nio.file.Path;
+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 ObfuscationPrimitiveNamesTest extends TestBase {
+
+ private static List<String> FORBIDDEN_NAMES =
+ Lists.newArrayList("int", "long", "boolean", "float");
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.print("Hello World!");
+ }
+ }
+
+ public static class A {}
+
+ public static class B {}
+
+ public static class C {}
+
+ public static class D {}
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public ObfuscationPrimitiveNamesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testNotHavingPrimitiveNames()
+ throws IOException, CompilationFailedException, ExecutionException {
+ Path dictionary = temp.getRoot().toPath().resolve("dictionary.txt");
+ FileUtils.writeTextFile(dictionary, FORBIDDEN_NAMES);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ObfuscationPrimitiveNamesTest.class)
+ .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput("Hello World!")
+ .inspect(
+ inspector -> {
+ assertEquals(5, inspector.allClasses().size());
+ assertTrue(
+ inspector.allClasses().stream()
+ .noneMatch(c -> FORBIDDEN_NAMES.contains(c.getFinalName())));
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 88a6e04..5cdbd87 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -137,15 +137,15 @@
ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
assertThat(superInterface1, isRenamed());
- MethodSubject foo = superInterface1.method("void", "foo", ImmutableList.of());
- assertThat(foo, isRenamed());
+ MethodSubject foo = superInterface1.uniqueMethodWithName("foo");
+ assertThat(foo, not(isPresent()));
ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
if (enableVerticalClassMerging) {
assertThat(superInterface2, not(isPresent()));
} else {
assertThat(superInterface2, isRenamed());
}
- MethodSubject bar = superInterface1.method("void", "bar", ImmutableList.of());
+ MethodSubject bar = superInterface2.uniqueMethodWithName("bar");
assertThat(bar, not(isPresent()));
ClassSubject subInterface = inspector.clazz(B112452064SubInterface.class);
if (enableUnusedInterfaceRemoval) {
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
index 555fc7c..f91de77 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
@@ -5,7 +5,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.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -16,7 +16,6 @@
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.Streams;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -125,17 +124,17 @@
MethodSubject mainMethod = main.mainMethod();
assertThat(mainMethod, isPresent());
- assertEquals(
- 0,
- Streams.stream(mainMethod.iterateInstructions(
- i -> i.isInvoke() && i.getMethod().name.toString().equals("m"))).count());
+ assertTrue(
+ mainMethod.streamInstructions().noneMatch(
+ i -> i.isInvoke() && i.getMethod().name.toString().equals("m")));
MethodSubject testInvokeInterface = main.uniqueMethodWithName("testInvokeInterface");
assertThat(testInvokeInterface, isPresent());
- assertEquals(
- 1,
- Streams.stream(testInvokeInterface.iterateInstructions(
- i -> i.isInvoke() && i.getMethod().name.toString().equals("m"))).count());
+ // With call site optimizations, the dynamic type of the argument is accurate (Impl1),
+ // hence the accurate resolution of the method call, resulting in rule application.
+ assertTrue(
+ testInvokeInterface.streamInstructions().noneMatch(
+ i -> i.isInvoke() && i.getMethod().name.toString().equals("m")));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
index 8430bb6..88d3b65 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking17Test.java
@@ -47,7 +47,11 @@
private static void abstractMethodRemains(CodeInspector inspector) {
ClassSubject programClass = inspector.clazz("shaking17.AbstractProgramClass");
Assert.assertTrue(programClass.isPresent());
- Assert.assertTrue(
+ // With call site optimization, the dynamic type of the argument of Shaking#callTheMethod is
+ // SubClass, not AbstractProgramClass. Then, the resolution of the invocation is accurately
+ // referring to SubClass#abstractMethod only, i.e., AbstractProgramClass#abstractMethod is no
+ // longer live, hence shrunken.
+ Assert.assertFalse(
programClass.method("int", "abstractMethod", Collections.emptyList()).isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index f2a924d..f28f242 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.shaking.TreeShakingTest;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -41,8 +40,7 @@
TreeShaking18Test::unusedRemoved,
null,
null,
- ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"),
- InternalOptions::enableCallSiteOptimizationInfoPropagation);
+ ImmutableList.of("src/test/examples/shaking18/keep-rules.txt"));
}
private static void unusedRemoved(CodeInspector inspector) {
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 2277036..d7e46b1 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
@@ -9,6 +9,7 @@
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.DexProto;
import com.android.tools.r8.graph.DexType;
@@ -203,6 +204,10 @@
return dexClass;
}
+ public ClassSubject getSuperClass() {
+ return codeInspector.clazz(dexClass.superType.toSourceString());
+ }
+
@Override
public AnnotationSubject annotation(String name) {
// Ensure we don't check for annotations represented as attributes.
@@ -233,6 +238,10 @@
}
}
+ public DexType getOriginalDexType(DexItemFactory dexItemFactory) {
+ return dexItemFactory.createType(getOriginalDescriptor());
+ }
+
@Override
public String getFinalName() {
return DescriptorUtils.descriptorToJavaType(getFinalDescriptor());
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index 0d63e38..a4d8210 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -97,6 +99,11 @@
return memberNaming != null ? (FieldSignature) memberNaming.getOriginalSignature() : signature;
}
+ public DexField getOriginalDexField(DexItemFactory dexItemFactory) {
+ FieldSignature fieldSignature = getOriginalSignature();
+ return fieldSignature.toDexField(dexItemFactory, clazz.getOriginalDexType(dexItemFactory));
+ }
+
@Override
public FieldSignature getFinalSignature() {
return FieldSignature.fromDexField(dexField.field);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 03e2755..0466c4e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -21,6 +21,8 @@
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexDebugPositionState;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming;
@@ -29,6 +31,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -174,11 +177,28 @@
dexMethod.annotations, GenericSignatureParser::parseMethodSignature);
}
+ public DexMethod getOriginalDexMethod(DexItemFactory dexItemFactory) {
+ MethodSignature methodSignature = getOriginalSignature();
+ if (methodSignature.isQualified()) {
+ methodSignature = methodSignature.toUnqualified();
+ }
+ return methodSignature.toDexMethod(
+ dexItemFactory, dexItemFactory.createType(clazz.getOriginalDescriptor()));
+ }
+
@Override
public String getFinalSignatureAttribute() {
return codeInspector.getFinalSignatureAttribute(dexMethod.annotations);
}
+ public Iterable<InstructionSubject> instructions() {
+ return instructions(Predicates.alwaysTrue());
+ }
+
+ public Iterable<InstructionSubject> instructions(Predicate<InstructionSubject> predicate) {
+ return () -> iterateInstructions(predicate);
+ }
+
@Override
public Iterator<InstructionSubject> iterateInstructions() {
return codeInspector.createInstructionIterator(this);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java b/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java
new file mode 100644
index 0000000..4a0f63d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.codeinspector.analysis;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.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.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.function.Function;
+
+public class ProtoApplicationStats {
+
+ private static final String EXTENDABLE_MESSAGE_TYPE =
+ "com.google.protobuf.GeneratedMessageLite$ExtendableMessage";
+ private static final String EXTENSION_REGISTRY_LITE_TYPE =
+ "com.google.protobuf.ExtensionRegistryLite";
+ private static final String GENERATED_EXTENSION_TYPE =
+ "com.google.protobuf.GeneratedMessageLite$GeneratedExtension";
+ private static final String GENERATED_MESSAGE_LITE_TYPE =
+ "com.google.protobuf.GeneratedMessageLite";
+ private static final String GENERATED_MESSAGE_LITE_BUILDER_TYPE =
+ GENERATED_MESSAGE_LITE_TYPE + "$Builder";
+
+ abstract static class Stats {
+
+ static <T> String progress(T actual, T baseline, T original, Function<T, Set<?>> fn) {
+ StringBuilder builder = new StringBuilder();
+ if (original != null) {
+ builder.append(fn.apply(original).size()).append(" -> ");
+ }
+ Set<?> actualSet = fn.apply(actual);
+ builder.append(actualSet.size());
+ if (baseline != null) {
+ Set<?> baselineSet = fn.apply(baseline);
+ builder
+ .append(" (unfulfilled potential: ")
+ .append(Sets.difference(actualSet, baselineSet).size())
+ .append(", improvement over baseline: ")
+ .append(Sets.difference(baselineSet, actualSet).size())
+ .append(" )");
+ }
+ return builder.toString();
+ }
+ }
+
+ class EnumStats extends Stats {
+
+ final Set<DexType> enums = Sets.newIdentityHashSet();
+
+ String getStats(EnumStats baseline, EnumStats original) {
+ return StringUtils.lines(
+ "Enum stats:", " # enums: " + progress(this, baseline, original, x -> x.enums));
+ }
+ }
+
+ class ProtoBuilderStats extends Stats {
+
+ final Set<DexType> builders = Sets.newIdentityHashSet();
+
+ String getStats(ProtoBuilderStats baseline, ProtoBuilderStats original) {
+ return StringUtils.lines(
+ "Proto builder stats:",
+ " # builders: " + progress(this, baseline, original, x -> x.builders));
+ }
+ }
+
+ class ProtoMessageStats extends Stats {
+
+ final boolean extendable;
+
+ ProtoMessageStats(boolean extendable) {
+ this.extendable = extendable;
+ }
+
+ final Set<DexType> messages = Sets.newIdentityHashSet();
+ final Set<DexField> bitFields = Sets.newIdentityHashSet();
+ final Set<DexField> nonBitFields = Sets.newIdentityHashSet();
+
+ String getStats(ProtoMessageStats baseline, ProtoMessageStats original) {
+ return StringUtils.lines(
+ extendable ? "Extendable proto message stats" : "Proto message stats:",
+ " # messages: " + progress(this, baseline, original, x -> x.messages),
+ " # bit fields: " + progress(this, baseline, original, x -> x.bitFields),
+ " # non-bit fields: " + progress(this, baseline, original, x -> x.nonBitFields));
+ }
+ }
+
+ class GeneratedExtensionRegistryStats extends Stats {
+
+ final Set<DexMethod> findLiteExtensionByNumberMethods = Sets.newIdentityHashSet();
+ final Set<DexType> retainedExtensions = Sets.newIdentityHashSet();
+ final Set<DexType> spuriouslyRetainedExtensions = Sets.newIdentityHashSet();
+
+ String getStats(
+ GeneratedExtensionRegistryStats baseline, GeneratedExtensionRegistryStats original) {
+ return StringUtils.lines(
+ "Generated extension registry stats:",
+ " # findLiteExtensionByNumber() methods: "
+ + progress(this, baseline, original, x -> x.findLiteExtensionByNumberMethods),
+ " # retained extensions: "
+ + progress(this, baseline, original, x -> x.retainedExtensions),
+ " # spuriously retained extensions: " + spuriouslyRetainedExtensions.size());
+ }
+ }
+
+ private final DexItemFactory dexItemFactory;
+ private final CodeInspector inspector;
+ private final ProtoApplicationStats original;
+
+ private final EnumStats enumStats = new EnumStats();
+ private final ProtoMessageStats extendableProtoMessageStats = new ProtoMessageStats(true);
+ private final GeneratedExtensionRegistryStats generatedExtensionRegistryStats =
+ new GeneratedExtensionRegistryStats();
+ private final ProtoBuilderStats protoBuilderStats = new ProtoBuilderStats();
+ private final ProtoMessageStats protoMessageStats = new ProtoMessageStats(false);
+
+ public ProtoApplicationStats(DexItemFactory dexItemFactory, CodeInspector inspector) {
+ this(dexItemFactory, inspector, null);
+ }
+
+ public ProtoApplicationStats(
+ DexItemFactory dexItemFactory, CodeInspector inspector, ProtoApplicationStats original) {
+ this.dexItemFactory = dexItemFactory;
+ this.inspector = inspector;
+ this.original = original;
+ computeStats();
+ }
+
+ private void computeStats() {
+ for (FoundClassSubject classSubject : inspector.allClasses()) {
+ DexType originalType = classSubject.getOriginalDexType(dexItemFactory);
+ if (classSubject.getDexClass().isEnum()) {
+ enumStats.enums.add(originalType);
+ }
+
+ ClassSubject superClassSubject = classSubject.getSuperClass();
+ if (!superClassSubject.isPresent()) {
+ continue;
+ }
+
+ ProtoMessageStats messageStats = null;
+ switch (superClassSubject.getOriginalName()) {
+ case GENERATED_MESSAGE_LITE_TYPE:
+ messageStats = protoMessageStats;
+ break;
+
+ case EXTENDABLE_MESSAGE_TYPE:
+ messageStats = extendableProtoMessageStats;
+ break;
+
+ case GENERATED_MESSAGE_LITE_BUILDER_TYPE:
+ protoBuilderStats.builders.add(
+ dexItemFactory.createType(classSubject.getOriginalDescriptor()));
+ break;
+
+ case EXTENSION_REGISTRY_LITE_TYPE:
+ for (FoundMethodSubject methodSubject : classSubject.allMethods()) {
+ String originalMethodName = methodSubject.getOriginalName(false);
+ if (originalMethodName.startsWith("findLiteExtensionByNumber")) {
+ generatedExtensionRegistryStats.findLiteExtensionByNumberMethods.add(
+ methodSubject.getOriginalDexMethod(dexItemFactory));
+
+ for (InstructionSubject instruction :
+ methodSubject.instructions(InstructionSubject::isStaticGet)) {
+ DexField field = instruction.getField();
+ FoundClassSubject typeClassSubject =
+ inspector.clazz(field.type.toSourceString()).asFoundClassSubject();
+ if (!typeClassSubject.getOriginalName().equals(GENERATED_EXTENSION_TYPE)) {
+ continue;
+ }
+ FoundClassSubject extensionClassSubject =
+ inspector.clazz(field.holder.toSourceString()).asFoundClassSubject();
+ generatedExtensionRegistryStats.retainedExtensions.add(
+ extensionClassSubject.getOriginalDexType(dexItemFactory));
+ }
+ }
+ }
+ break;
+ }
+
+ if (messageStats != null) {
+ messageStats.messages.add(originalType);
+ for (FoundFieldSubject fieldSubject : classSubject.allInstanceFields()) {
+ String originalFieldName = fieldSubject.getOriginalName(false);
+ if (originalFieldName.startsWith("bitField")) {
+ messageStats.bitFields.add(fieldSubject.getOriginalDexField(dexItemFactory));
+ } else {
+ messageStats.nonBitFields.add(fieldSubject.getOriginalDexField(dexItemFactory));
+ }
+ }
+ }
+ }
+
+ if (original != null) {
+ for (DexType extensionType : original.generatedExtensionRegistryStats.retainedExtensions) {
+ if (!generatedExtensionRegistryStats.retainedExtensions.contains(extensionType)
+ && inspector.clazz(extensionType.toSourceString()).isPresent()) {
+ generatedExtensionRegistryStats.spuriouslyRetainedExtensions.add(extensionType);
+ }
+ }
+ }
+ }
+
+ public String getStats() {
+ return StringUtils.lines(
+ enumStats.getStats(null, original.enumStats),
+ protoMessageStats.getStats(null, original.protoMessageStats),
+ extendableProtoMessageStats.getStats(null, original.extendableProtoMessageStats),
+ protoBuilderStats.getStats(null, original.protoBuilderStats),
+ generatedExtensionRegistryStats.getStats(null, original.generatedExtensionRegistryStats));
+ }
+
+ public String getStats(ProtoApplicationStats baseline) {
+ return StringUtils.lines(
+ enumStats.getStats(baseline.enumStats, original.enumStats),
+ protoMessageStats.getStats(baseline.protoMessageStats, original.protoMessageStats),
+ extendableProtoMessageStats.getStats(
+ baseline.extendableProtoMessageStats, original.extendableProtoMessageStats),
+ protoBuilderStats.getStats(baseline.protoBuilderStats, original.protoBuilderStats),
+ generatedExtensionRegistryStats.getStats(
+ baseline.generatedExtensionRegistryStats, original.generatedExtensionRegistryStats));
+ }
+}
diff --git a/third_party/opensource_apps.tar.gz.sha1 b/third_party/opensource_apps.tar.gz.sha1
index 6bc1780..a6f2eb2 100644
--- a/third_party/opensource_apps.tar.gz.sha1
+++ b/third_party/opensource_apps.tar.gz.sha1
@@ -1 +1 @@
-bc2e91a9f03b33eeeec6e224c1bd73d1d15d7a39
\ No newline at end of file
+792d03aef34debed948aa021de6f77e3dd07b267
\ No newline at end of file
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 89d0da2..8b87f84 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -307,6 +307,39 @@
]
}),
Repo({
+ 'name': 'Simple-Camera',
+ 'url': 'https://github.com/jsjeon/Simple-Camera',
+ 'revision': '451fe188ab123e6956413b42e89839b44c05ac14',
+ 'apps': [
+ App({
+ 'id': 'com.simplemobiletools.camera.pro',
+ 'signed_apk_name': 'camera-release.apk'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Simple-File-Manager',
+ 'url': 'https://github.com/jsjeon/Simple-File-Manager',
+ 'revision': '282b57d9e73f4d250cc844d8d73fd223509a141e',
+ 'apps': [
+ App({
+ 'id': 'com.simplemobiletools.filemanager.pro',
+ 'signed_apk_name': 'file-manager-release.apk'
+ })
+ ]
+ }),
+ Repo({
+ 'name': 'Simple-Gallery',
+ 'url': 'https://github.com/jsjeon/Simple-Gallery',
+ 'revision': '679125601eee7e057dfdfecd7bea6c4a6ac73ef9',
+ 'apps': [
+ App({
+ 'id': 'com.simplemobiletools.gallery.pro',
+ 'signed_apk_name': 'gallery-release.apk'
+ })
+ ]
+ }),
+ Repo({
'name': 'sqldelight',
'url': 'https://github.com/christofferqa/sqldelight.git',
'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',