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',