Merge commit '1932e57c7bf58f96aed8c3e06fad612c6e6f817d' into dev-release
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json index dc7ac7b..3a9c0b4 100644 --- a/src/library_desugar/desugar_jdk_libs.json +++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@ "configuration_format_version": 3, "group_id" : "com.tools.android", "artifact_id" : "desugar_jdk_libs", - "version": "1.1.4", + "version": "1.1.5", "required_compilation_api_level": 26, "synthesized_library_classes_package_prefix": "j$.", "support_all_callbacks_from_library": true, @@ -77,12 +77,6 @@ "java.time.": "j$.time.", "java.util.Desugar": "j$.util.Desugar" }, - "backport": { - "java.lang.Double8": "java.lang.Double", - "java.lang.Integer8": "java.lang.Integer", - "java.lang.Long8": "java.lang.Long", - "java.lang.Math8": "java.lang.Math" - }, "retarget_lib_member": { "java.util.Date#toInstant": "java.util.DesugarDate", "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index b552bb2..4c235d9 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; +import com.google.common.collect.ImmutableList; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -315,6 +316,9 @@ } r8Builder.addProguardConfiguration( libraryConfiguration.getExtraKeepRules(), Origin.unknown()); + // TODO(b/180903899): Remove rule when -dontwarn sun.misc.Unsafe is part of config. + r8Builder.addProguardConfiguration( + ImmutableList.of("-dontwarn sun.misc.Unsafe"), Origin.unknown()); r8Builder.addProguardConfigurationFiles(proguardConfigFiles); r8Builder.setDisableDesugaring(true); r8Builder.skipDump();
diff --git a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java deleted file mode 100644 index b46a2ba..0000000 --- a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.TYPE) -public @interface SynthesizedClassMap { - Class<?>[] value() default {}; -}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java index 8f56827..614ac4d 100644 --- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java +++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -467,7 +467,7 @@ // Pull out the classes that should go into feature splits. Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses = - classToFeatureSplitMap.getFeatureSplitClasses(classes); + classToFeatureSplitMap.getFeatureSplitClasses(classes, appView.getSyntheticItems()); if (featureSplitClasses.size() > 0) { for (Set<DexProgramClass> featureClasses : featureSplitClasses.values()) { classes.removeAll(featureClasses); @@ -475,14 +475,17 @@ } List<DexProgramClass> toRemove = new ArrayList<>(); for (DexProgramClass dexProgramClass : classes) { - Collection<DexProgramClass> synthesizedFrom = dexProgramClass.getSynthesizedFrom(); - if (!synthesizedFrom.isEmpty()) { - DexProgramClass from = Iterables.getFirst(synthesizedFrom, null); - FeatureSplit featureSplit = classToFeatureSplitMap.getFeatureSplit(from); - if (!featureSplit.isBase()) { - Set<DexProgramClass> dexProgramClasses = featureSplitClasses.get(featureSplit); - dexProgramClasses.add(dexProgramClass); - toRemove.add(dexProgramClass); + if (appView.getSyntheticItems().isLegacySyntheticClass(dexProgramClass)) { + Collection<DexProgramClass> synthesizedFrom = dexProgramClass.getSynthesizedFrom(); + if (!synthesizedFrom.isEmpty()) { + DexProgramClass from = Iterables.getFirst(synthesizedFrom, null); + FeatureSplit featureSplit = + classToFeatureSplitMap.getFeatureSplit(from, appView.getSyntheticItems()); + if (!featureSplit.isBase()) { + Set<DexProgramClass> dexProgramClasses = featureSplitClasses.get(featureSplit); + dexProgramClasses.add(dexProgramClass); + toRemove.add(dexProgramClass); + } } } }
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java index 1ea1595..d96643d 100644 --- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java +++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -17,11 +17,15 @@ import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.PrunedItems; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Reporter; import com.google.common.collect.Sets; +import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -88,9 +92,9 @@ return result; } - public int compareFeatureSplitsForDexTypes(DexType a, DexType b) { - FeatureSplit featureSplitA = getFeatureSplit(a); - FeatureSplit featureSplitB = getFeatureSplit(b); + public int compareFeatureSplitsForDexTypes(DexType a, DexType b, SyntheticItems syntheticItems) { + FeatureSplit featureSplitA = getFeatureSplit(a, syntheticItems); + FeatureSplit featureSplitB = getFeatureSplit(b, syntheticItems); assert featureSplitA != null; assert featureSplitB != null; if (featureSplitA == featureSplitB) { @@ -109,10 +113,10 @@ } public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses( - Set<DexProgramClass> classes) { + Set<DexProgramClass> classes, SyntheticItems syntheticItems) { Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>(); for (DexProgramClass clazz : classes) { - FeatureSplit featureSplit = getFeatureSplit(clazz); + FeatureSplit featureSplit = getFeatureSplit(clazz, syntheticItems); if (featureSplit != null && !featureSplit.isBase()) { result.computeIfAbsent(featureSplit, ignore -> Sets.newIdentityHashSet()).add(clazz); } @@ -120,41 +124,61 @@ return result; } - public FeatureSplit getFeatureSplit(ProgramDefinition clazz) { - return getFeatureSplit(clazz.getContextType()); + public FeatureSplit getFeatureSplit(ProgramDefinition clazz, SyntheticItems syntheticItems) { + return getFeatureSplit(clazz.getContextType(), syntheticItems); } - public FeatureSplit getFeatureSplit(DexType type) { + public FeatureSplit getFeatureSplit(DexType type, SyntheticItems syntheticItems) { + Collection<DexType> contexts = syntheticItems.getSynthesizingContexts(type); + if (!contexts.isEmpty()) { + Iterator<DexType> iterator = contexts.iterator(); + DexType context = iterator.next(); + FeatureSplit feature = classToFeatureSplitMap.getOrDefault(context, FeatureSplit.BASE); + assert verifyConsistentFeatureContexts(iterator, feature); + return feature; + } return classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE); } + private boolean verifyConsistentFeatureContexts( + Iterator<DexType> contextIterator, FeatureSplit feature) { + while (contextIterator.hasNext()) { + assert feature + == classToFeatureSplitMap.getOrDefault(contextIterator.next(), FeatureSplit.BASE); + } + return true; + } + public boolean isEmpty() { return classToFeatureSplitMap.isEmpty(); } - public boolean isInBase(DexProgramClass clazz) { - return getFeatureSplit(clazz).isBase(); + public boolean isInBase(DexProgramClass clazz, SyntheticItems syntheticItems) { + return getFeatureSplit(clazz, syntheticItems).isBase(); } - public boolean isInBaseOrSameFeatureAs(DexProgramClass clazz, ProgramDefinition context) { - FeatureSplit split = getFeatureSplit(clazz); - return split.isBase() || split == getFeatureSplit(context); + public boolean isInBaseOrSameFeatureAs( + DexProgramClass clazz, ProgramDefinition context, SyntheticItems syntheticItems) { + FeatureSplit split = getFeatureSplit(clazz, syntheticItems); + return split.isBase() || split == getFeatureSplit(context, syntheticItems); } - public boolean isInFeature(DexProgramClass clazz) { - return !isInBase(clazz); + public boolean isInFeature(DexProgramClass clazz, SyntheticItems syntheticItems) { + return !isInBase(clazz, syntheticItems); } - public boolean isInSameFeatureOrBothInBase(ProgramMethod a, ProgramMethod b) { - return isInSameFeatureOrBothInBase(a.getHolder(), b.getHolder()); + public boolean isInSameFeatureOrBothInBase( + ProgramMethod a, ProgramMethod b, SyntheticItems syntheticItems) { + return isInSameFeatureOrBothInBase(a.getHolder(), b.getHolder(), syntheticItems); } - public boolean isInSameFeatureOrBothInBase(DexProgramClass a, DexProgramClass b) { - return getFeatureSplit(a) == getFeatureSplit(b); + public boolean isInSameFeatureOrBothInBase( + DexProgramClass a, DexProgramClass b, SyntheticItems syntheticItems) { + return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems); } - public boolean isInSameFeatureOrBothInBase(DexType a, DexType b) { - return getFeatureSplit(a) == getFeatureSplit(b); + public boolean isInSameFeatureOrBothInBase(DexType a, DexType b, SyntheticItems syntheticItems) { + return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems); } public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) { @@ -187,4 +211,14 @@ }); return classToFeatureSplitMapAfterPruning; } + + // Static helpers to avoid verbose predicates. + + private static ClassToFeatureSplitMap getMap(AppView<AppInfoWithLiveness> appView) { + return appView.appInfo().getClassToFeatureSplitMap(); + } + + public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) { + return getMap(appView).isInFeature(clazz, appView.getSyntheticItems()); + } }
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java index f872a56..51ff1e2 100644 --- a/src/main/java/com/android/tools/r8/graph/AccessControl.java +++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.graph; import com.android.tools.r8.features.ClassToFeatureSplitMap; +import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.utils.OptionalBool; /** @@ -18,16 +19,21 @@ DexClass clazz, ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) { - return isClassAccessible(clazz, context, appView.appInfo().getClassToFeatureSplitMap()); + return isClassAccessible( + clazz, context, appView.appInfo().getClassToFeatureSplitMap(), appView.getSyntheticItems()); } public static OptionalBool isClassAccessible( - DexClass clazz, ProgramDefinition context, ClassToFeatureSplitMap classToFeatureSplitMap) { + DexClass clazz, + ProgramDefinition context, + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) { return OptionalBool.FALSE; } if (clazz.isProgramClass() - && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(clazz.asProgramClass(), context)) { + && !classToFeatureSplitMap.isInBaseOrSameFeatureAs( + clazz.asProgramClass(), context, syntheticItems)) { return OptionalBool.UNKNOWN; } return OptionalBool.TRUE; @@ -60,7 +66,11 @@ AppInfoWithClassHierarchy appInfo) { AccessFlags<?> memberFlags = member.getDefinition().getAccessFlags(); OptionalBool classAccessibility = - isClassAccessible(initialResolutionHolder, context, appInfo.getClassToFeatureSplitMap()); + isClassAccessible( + initialResolutionHolder, + context, + appInfo.getClassToFeatureSplitMap(), + appInfo.getSyntheticItems()); if (classAccessibility.isFalse()) { return OptionalBool.FALSE; }
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java index bb61928..2cfb4d9 100644 --- a/src/main/java/com/android/tools/r8/graph/AppServices.java +++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -105,12 +105,14 @@ } // Check if service is defined feature DexProgramClass serviceClass = appView.definitionForProgramType(serviceType); - if (serviceClass != null && classToFeatureSplitMap.isInFeature(serviceClass)) { + if (serviceClass != null + && classToFeatureSplitMap.isInFeature(serviceClass, appView.getSyntheticItems())) { return true; } for (DexType implementationType : featureImplementations.get(FeatureSplit.BASE)) { DexProgramClass implementationClass = appView.definitionForProgramType(implementationType); - if (implementationClass != null && classToFeatureSplitMap.isInFeature(implementationClass)) { + if (implementationClass != null + && classToFeatureSplitMap.isInFeature(implementationClass, appView.getSyntheticItems())) { return true; } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java index 3483fbb..f2959bb 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -5,7 +5,6 @@ import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.dex.MixedSectionCollection; -import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.graph.DexValue.DexValueAnnotation; import com.android.tools.r8.graph.DexValue.DexValueArray; import com.android.tools.r8.graph.DexValue.DexValueInt; @@ -22,10 +21,7 @@ import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.TreeSet; import java.util.function.Function; public class DexAnnotation extends DexItem implements StructuralItem<DexAnnotation> { @@ -96,8 +92,7 @@ } if (annotation == options.itemFactory.dalvikFastNativeAnnotation || annotation == options.itemFactory.dalvikCriticalNativeAnnotation - || annotation == options.itemFactory.annotationSynthesizedClass - || annotation == options.itemFactory.annotationSynthesizedClassMap) { + || annotation == options.itemFactory.annotationSynthesizedClass) { return true; } if (options.processCovariantReturnTypeAnnotations) { @@ -363,43 +358,6 @@ return new DexValueString(factory.createString(string)); } - public static Collection<DexType> readAnnotationSynthesizedClassMap( - DexProgramClass clazz, DexItemFactory dexItemFactory) { - DexAnnotation foundAnnotation = - clazz.annotations().getFirstMatching(dexItemFactory.annotationSynthesizedClassMap); - if (foundAnnotation != null) { - if (foundAnnotation.annotation.elements.length != 1) { - throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation)); - } - DexAnnotationElement value = foundAnnotation.annotation.elements[0]; - if (value.name != dexItemFactory.valueString) { - throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation)); - } - DexValueArray existingList = value.value.asDexValueArray(); - if (existingList == null) { - throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation)); - } - Collection<DexType> synthesized = new ArrayList<>(existingList.values.length); - for (DexValue element : existingList.getValues()) { - if (!element.isDexValueType()) { - throw new CompilationError(getInvalidSynthesizedClassMapMessage(clazz, foundAnnotation)); - } - synthesized.add(element.asDexValueType().value); - } - return synthesized; - } - return Collections.emptyList(); - } - - private static String getInvalidSynthesizedClassMapMessage( - DexProgramClass annotatedClass, - DexAnnotation invalidAnnotation) { - return annotatedClass.toSourceString() - + " is annotated with invalid " - + invalidAnnotation.annotation.type.toString() - + ": " + invalidAnnotation.toString(); - } - public static DexAnnotation createAnnotationSynthesizedClass( SyntheticKind kind, DexType synthesizingContext, DexItemFactory dexItemFactory) { DexAnnotationElement kindElement = @@ -456,26 +414,6 @@ return new Pair<>(kind, valueElement.value.asDexValueType().getValue()); } - public static DexAnnotation createAnnotationSynthesizedClassMap( - TreeSet<DexType> synthesized, - DexItemFactory dexItemFactory) { - DexValueType[] values = synthesized.stream() - .map(DexValueType::new) - .toArray(DexValueType[]::new); - DexValueArray array = new DexValueArray(values); - DexAnnotationElement pair = - new DexAnnotationElement(dexItemFactory.createString("value"), array); - return new DexAnnotation( - VISIBILITY_BUILD, - new DexEncodedAnnotation( - dexItemFactory.annotationSynthesizedClassMap, new DexAnnotationElement[]{pair})); - } - - public static boolean isSynthesizedClassMapAnnotation(DexAnnotation annotation, - DexItemFactory factory) { - return annotation.annotation.type == factory.annotationSynthesizedClassMap; - } - public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) { DexEncodedAnnotation rewritten = rewriter.apply(annotation); if (rewritten == annotation) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java index 0752d20..3f6eff3 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -605,8 +605,6 @@ public final DexType annotationThrows = createStaticallyKnownType("Ldalvik/annotation/Throws;"); public final DexType annotationSynthesizedClass = createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClass;"); - public final DexType annotationSynthesizedClassMap = - createStaticallyKnownType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;"); public final DexType annotationCovariantReturnType = createStaticallyKnownType("Ldalvik/annotation/codegen/CovariantReturnType;"); public final DexType annotationCovariantReturnTypes =
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java index 9e4dd2b..20caa50 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -19,6 +19,9 @@ @Override public FeatureSplit getMergeKey(DexProgramClass clazz) { - return appView.appInfo().getClassToFeatureSplitMap().getFeatureSplit(clazz); + return appView + .appInfo() + .getClassToFeatureSplitMap() + .getFeatureSplit(clazz, appView.getSyntheticItems()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index 969b5d5..33e414f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -38,6 +38,7 @@ import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Timing; @@ -152,10 +153,11 @@ return false; } + SyntheticItems syntheticItems = appView.getSyntheticItems(); ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap(); - if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(singleTarget, method)) { + if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(singleTarget, method, syntheticItems)) { // Still allow inlining if we inline from the base into a feature. - if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder())) { + if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder(), syntheticItems)) { whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit(); return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java index 8dc0dd3..0f12d78 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.contexts.CompilationContext.ProcessorContext; import com.android.tools.r8.dex.Constants; import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.features.ClassToFeatureSplitMap; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ClasspathMethod; import com.android.tools.r8.graph.Code; @@ -1287,7 +1288,7 @@ code -> { ProgramMethod context = code.context(); assert !context.getDefinition().getCode().isOutlineCode(); - if (appView.appInfo().getClassToFeatureSplitMap().isInFeature(context.getHolder())) { + if (ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView)) { return; } for (BasicBlock block : code.blocks) { @@ -1306,7 +1307,7 @@ public void identifyOutlineSites(IRCode code) { ProgramMethod context = code.context(); assert !context.getDefinition().getCode().isOutlineCode(); - assert !appView.appInfo().getClassToFeatureSplitMap().isInFeature(context.getHolder()); + assert !ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView); for (BasicBlock block : code.blocks) { new OutlineSiteIdentifier(context, block).process(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java index afdf56a..289b2ba 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -246,7 +246,8 @@ // Make sure the (base) type is visible. ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap(); - if (AccessControl.isClassAccessible(baseClass, context, classToFeatureSplitMap) + if (AccessControl.isClassAccessible( + baseClass, context, classToFeatureSplitMap, appView.getSyntheticItems()) .isPossiblyFalse()) { return; } @@ -254,7 +255,8 @@ // If the type is guaranteed to be visible, it must be in the same feature as the current method // or in the base. assert !baseClass.isProgramClass() - || classToFeatureSplitMap.isInBaseOrSameFeatureAs(baseClass.asProgramClass(), context); + || classToFeatureSplitMap.isInBaseOrSameFeatureAs( + baseClass.asProgramClass(), context, appView.getSyntheticItems()); consumer.accept(type, baseClass); }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java index 0b340af..84acf70 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -177,6 +177,7 @@ DexType parameter = sourceParameters[i]; ValueType parameterType = ValueType.fromDexType(parameter); instructions.add(new CfLoad(parameterType, maxLocals)); + maxStack += parameterType.requiredRegisters(); maxLocals += parameterType.requiredRegisters(); maybeInsertArgumentCast(i, parameter, instructions); }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java index 762617e..057a80f 100644 --- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java +++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -115,10 +115,6 @@ return isAnnotationTypeLive; case DexAnnotation.VISIBILITY_BUILD: - if (DexAnnotation.isSynthesizedClassMapAnnotation(annotation, dexItemFactory)) { - // TODO(sgjesse) When should these be removed? - return true; - } if (!config.runtimeInvisibleAnnotations) { return false; }
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java index 58315d4..a60967f 100644 --- a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
@@ -91,6 +91,11 @@ return this == NONE; } + public boolean isMainDexTypeThatShouldIncludeDependencies(DexType type) { + // Dependencies of 'type' are only needed if 'type' is a direct/executed main-dex type. + return classList.contains(type) || tracedRoots.contains(type); + } + public boolean isMainDex(ProgramDefinition definition) { return isFromList(definition) || isTracedRoot(definition) || isDependency(definition); }
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java index c2d6a7c..ba25312 100644 --- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java +++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -73,10 +73,6 @@ // if the same type is in newMissingClasses in which case it is reported regardless. private final Set<DexType> newIgnoredMissingClasses = Sets.newIdentityHashSet(); - private Builder() { - this(Sets.newIdentityHashSet()); - } - private Builder(Set<DexType> alreadyMissingClasses) { this.alreadyMissingClasses = alreadyMissingClasses; } @@ -133,9 +129,7 @@ getMissingClassesToBeReported(appView, synthesizingContextOracle); if (!missingClassesToBeReported.isEmpty()) { MissingDefinitionsDiagnostic diagnostic = createDiagnostic(missingClassesToBeReported); - InternalOptions options = appView.options(); - // TODO(b/180903899): Remove L8 special handling when -dontwarn sun.misc.Unsafe is in place. - if (options.ignoreMissingClasses || options.isDesugaredLibraryCompilation()) { + if (appView.options().ignoreMissingClasses) { appView.reporter().warning(diagnostic); } else { throw appView.reporter().fatalError(diagnostic); @@ -270,7 +264,6 @@ dexItemFactory.annotationMethodParameters, dexItemFactory.annotationSourceDebugExtension, dexItemFactory.annotationSynthesizedClass, - dexItemFactory.annotationSynthesizedClassMap, dexItemFactory.annotationThrows, dexItemFactory.serializedLambdaType) .addAll(dexItemFactory.getJavaConversionTypes())
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index 4963f21..afc9e50 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -357,7 +357,7 @@ if (!appInfo .getClassToFeatureSplitMap() - .isInSameFeatureOrBothInBase(sourceClass, targetClass)) { + .isInSameFeatureOrBothInBase(sourceClass, targetClass, appView.getSyntheticItems())) { return false; } if (appView.appServices().allServiceTypes().contains(sourceClass.type)
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java index 82f3204..bff9202 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java +++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -15,7 +15,6 @@ import com.android.tools.r8.origin.Origin; import com.android.tools.r8.shaking.MainDexInfo; import java.util.Comparator; -import java.util.Set; /** * A synthesizing context is a description of the context that gives rise to a synthetic item. @@ -123,14 +122,15 @@ appView.rewritePrefix.rewriteType(hygienicType, rewrittenType); } + // TODO(b/180074885): Remove this once main-dex is traced at the end of of compilation. void addIfDerivedFromMainDexClass( - DexProgramClass externalSyntheticClass, - MainDexInfo mainDexInfo, - Set<DexType> allMainDexTypes) { + DexProgramClass externalSyntheticClass, MainDexInfo mainDexInfo) { + if (mainDexInfo.isMainDex(externalSyntheticClass)) { + return; + } // The input context type (not the annotated context) determines if the derived class is to be - // in main dex. - // TODO(b/168584485): Once resolved allMainDexTypes == mainDexClasses. - if (allMainDexTypes.contains(inputContextType)) { + // in main dex, as it is the input context type that is traced as part of main-dex tracing. + if (mainDexInfo.isMainDexTypeThatShouldIncludeDependencies(inputContextType)) { mainDexInfo.addSyntheticClass(externalSyntheticClass); } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java index 9520d57..f6577d3 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -66,7 +66,10 @@ public abstract C getHolder(); final HashCode computeHash( - RepresentativeMap map, boolean intermediate, ClassToFeatureSplitMap classToFeatureSplitMap) { + RepresentativeMap map, + boolean intermediate, + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { Hasher hasher = Hashing.murmur3_128().newHasher(); if (getKind().isFixedSuffixSynthetic) { // Fixed synthetics are non-shareable. Its unique type is used as the hash key. @@ -81,7 +84,7 @@ if (!classToFeatureSplitMap.isEmpty()) { hasher.putInt( classToFeatureSplitMap - .getFeatureSplit(getContext().getSynthesizingContextType()) + .getFeatureSplit(getContext().getSynthesizingContextType(), syntheticItems) .hashCode()); } @@ -95,15 +98,17 @@ D other, boolean includeContext, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { - return compareTo(other, includeContext, graphLens, classToFeatureSplitMap) == 0; + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { + return compareTo(other, includeContext, graphLens, classToFeatureSplitMap, syntheticItems) == 0; } int compareTo( D other, boolean includeContext, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { DexType thisType = getHolder().getType(); DexType otherType = other.getHolder().getType(); if (getKind().isFixedSuffixSynthetic) { @@ -120,10 +125,10 @@ DexType synthesizingContextType = this.getContext().getSynthesizingContextType(); DexType otherSynthesizingContextType = other.getContext().getSynthesizingContextType(); if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase( - synthesizingContextType, otherSynthesizingContextType)) { + synthesizingContextType, otherSynthesizingContextType, syntheticItems)) { int order = classToFeatureSplitMap.compareFeatureSplitsForDexTypes( - synthesizingContextType, otherSynthesizingContextType); + synthesizingContextType, otherSynthesizingContextType, syntheticItems); assert order != 0; return order; }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java index 60dca8f..2e70db6 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedMethod; @@ -32,11 +31,9 @@ import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; import com.android.tools.r8.utils.structural.RepresentativeMap; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; import com.google.common.collect.Sets; import com.google.common.hash.HashCode; import java.util.ArrayList; @@ -48,7 +45,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.TreeSet; import java.util.function.BiConsumer; import java.util.function.Function; @@ -203,9 +199,11 @@ public int compareToIncludingContext( EquivalenceGroup<T> other, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { return getRepresentative() - .compareTo(other.getRepresentative(), true, graphLens, classToFeatureSplitMap); + .compareTo( + other.getRepresentative(), true, graphLens, classToFeatureSplitMap, syntheticItems); } @Override @@ -219,11 +217,14 @@ } private final InternalOptions options; - private final CommittedSyntheticsCollection synthetics; + private final SyntheticItems synthetics; + private final CommittedSyntheticsCollection committed; - SyntheticFinalization(InternalOptions options, CommittedSyntheticsCollection synthetics) { + SyntheticFinalization( + InternalOptions options, SyntheticItems synthetics, CommittedSyntheticsCollection committed) { this.options = options; this.synthetics = synthetics; + this.committed = committed; } public static void finalize(AppView<AppInfo> appView) { @@ -257,7 +258,6 @@ Result computeFinalSynthetics(AppView<?> appView) { assert verifyNoNestedSynthetics(); DexApplication application; - MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo(); Builder lensBuilder = new Builder(); ImmutableMap.Builder<DexType, SyntheticMethodReference> finalMethodsBuilder = ImmutableMap.builder(); @@ -269,8 +269,8 @@ application = buildLensAndProgram( appView, - computeEquivalences(appView, synthetics.getNonLegacyMethods().values(), generators), - computeEquivalences(appView, synthetics.getNonLegacyClasses().values(), generators), + computeEquivalences(appView, committed.getNonLegacyMethods().values(), generators), + computeEquivalences(appView, committed.getNonLegacyClasses().values(), generators), lensBuilder, (clazz, reference) -> { finalSyntheticProgramDefinitions.add(clazz); @@ -285,13 +285,8 @@ ImmutableMap<DexType, SyntheticProgramClassReference> finalClasses = finalClassesBuilder.build(); - handleSynthesizedClassMapping( - finalSyntheticProgramDefinitions, application, options, mainDexInfo, lensBuilder.typeMap); - - assert appView.appInfo().getMainDexInfo() == mainDexInfo; - Set<DexType> prunedSynthetics = Sets.newIdentityHashSet(); - synthetics.forEachNonLegacyItem( + committed.forEachNonLegacyItem( reference -> { DexType type = reference.getHolder(); if (!finalMethods.containsKey(type) && !finalClasses.containsKey(type)) { @@ -304,7 +299,7 @@ SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION, application, new CommittedSyntheticsCollection( - synthetics.getLegacyTypes(), finalMethods, finalClasses), + committed.getLegacyTypes(), finalMethods, finalClasses), ImmutableList.of()), lensBuilder.build(appView.graphLens(), appView.dexItemFactory()), PrunedItems.builder() @@ -330,114 +325,25 @@ intermediate, appView.dexItemFactory(), appView.graphLens(), - classToFeatureSplitMap); + classToFeatureSplitMap, + synthetics); return computeActualEquivalences( potentialEquivalences, generators, appView, intermediate, classToFeatureSplitMap); } private boolean isNotSyntheticType(DexType type) { - return !synthetics.containsNonLegacyType(type); + return !committed.containsNonLegacyType(type); } private boolean verifyNoNestedSynthetics() { // Check that a context is never itself synthetic class. - synthetics.forEachNonLegacyItem( + committed.forEachNonLegacyItem( item -> { assert isNotSyntheticType(item.getContext().getSynthesizingContextType()); }); return true; } - private void handleSynthesizedClassMapping( - List<DexProgramClass> finalSyntheticClasses, - DexApplication application, - InternalOptions options, - MainDexInfo mainDexInfo, - Map<DexType, DexType> derivedMainDexTypesToIgnore) { - boolean includeSynthesizedClassMappingInOutput = shouldAnnotateSynthetics(options); - if (includeSynthesizedClassMappingInOutput) { - updateSynthesizedClassMapping(application, finalSyntheticClasses); - } - updateMainDexListWithSynthesizedClassMap(application, mainDexInfo, derivedMainDexTypesToIgnore); - if (!includeSynthesizedClassMappingInOutput) { - clearSynthesizedClassMapping(application); - } - } - - private void updateSynthesizedClassMapping( - DexApplication application, List<DexProgramClass> finalSyntheticClasses) { - ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized = - ArrayListMultimap.create(); - for (DexType type : synthetics.getLegacyTypes()) { - DexProgramClass clazz = DexProgramClass.asProgramClassOrNull(application.definitionFor(type)); - if (clazz != null) { - for (DexProgramClass origin : clazz.getSynthesizedFrom()) { - originalToSynthesized.put(origin, clazz); - } - } - } - for (DexProgramClass clazz : finalSyntheticClasses) { - for (DexProgramClass origin : clazz.getSynthesizedFrom()) { - originalToSynthesized.put(origin, clazz); - } - } - for (Map.Entry<DexProgramClass, Collection<DexProgramClass>> entry : - originalToSynthesized.asMap().entrySet()) { - DexProgramClass original = entry.getKey(); - // Use a tree set to make sure that we have an ordering on the types. - // These types are put in an array in annotations in the output and we - // need a consistent ordering on them. - TreeSet<DexType> synthesized = new TreeSet<>(DexType::compareTo); - entry.getValue().stream() - .map(dexProgramClass -> dexProgramClass.type) - .forEach(synthesized::add); - synthesized.addAll( - DexAnnotation.readAnnotationSynthesizedClassMap(original, application.dexItemFactory)); - - DexAnnotation updatedAnnotation = - DexAnnotation.createAnnotationSynthesizedClassMap( - synthesized, application.dexItemFactory); - - original.setAnnotations(original.annotations().getWithAddedOrReplaced(updatedAnnotation)); - } - } - - private void updateMainDexListWithSynthesizedClassMap( - DexApplication application, - MainDexInfo mainDexInfo, - Map<DexType, DexType> derivedMainDexTypesToIgnore) { - if (mainDexInfo.isEmpty()) { - return; - } - List<DexProgramClass> newMainDexClasses = new ArrayList<>(); - mainDexInfo.forEachExcludingDependencies( - dexType -> { - DexProgramClass programClass = - DexProgramClass.asProgramClassOrNull(application.definitionFor(dexType)); - if (programClass != null) { - Collection<DexType> derived = - DexAnnotation.readAnnotationSynthesizedClassMap( - programClass, application.dexItemFactory); - for (DexType type : derived) { - DexType mappedType = derivedMainDexTypesToIgnore.getOrDefault(type, type); - DexProgramClass syntheticClass = - DexProgramClass.asProgramClassOrNull(application.definitionFor(mappedType)); - if (syntheticClass != null) { - newMainDexClasses.add(syntheticClass); - } - } - } - }); - newMainDexClasses.forEach(mainDexInfo::addSyntheticClass); - } - - private void clearSynthesizedClassMapping(DexApplication application) { - for (DexProgramClass clazz : application.classes()) { - clazz.setAnnotations( - clazz.annotations().getWithout(application.dexItemFactory.annotationSynthesizedClassMap)); - } - } - private static DexApplication buildLensAndProgram( AppView<?> appView, Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups, @@ -448,23 +354,8 @@ DexApplication application = appView.appInfo().app(); DexItemFactory factory = appView.dexItemFactory(); List<DexProgramClass> newProgramClasses = new ArrayList<>(); - - // TODO(b/168584485): Remove this once class-mapping support is removed. - Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet(); - MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo(); - mainDexInfo.forEachExcludingDependencies( - mainDexType -> { - derivedMainDexTypes.add(mainDexType); - DexProgramClass mainDexClass = - DexProgramClass.asProgramClassOrNull( - appView.appInfo().definitionForWithoutExistenceAssert(mainDexType)); - if (mainDexClass != null) { - derivedMainDexTypes.addAll( - DexAnnotation.readAnnotationSynthesizedClassMap(mainDexClass, factory)); - } - }); - Set<DexType> pruned = Sets.newIdentityHashSet(); + syntheticMethodGroups.forEach( (syntheticType, syntheticGroup) -> { SyntheticMethodDefinition representative = syntheticGroup.getRepresentative(); @@ -565,8 +456,7 @@ addMainDexAndSynthesizedFromForMember( member, externalSyntheticClass, - mainDexInfo, - derivedMainDexTypes, + appView.appInfo().getMainDexInfo(), appForLookup::programDefinitionFor); } }); @@ -587,8 +477,7 @@ addMainDexAndSynthesizedFromForMember( member, externalSyntheticClass, - mainDexInfo, - derivedMainDexTypes, + appView.appInfo().getMainDexInfo(), appForLookup::programDefinitionFor); } }); @@ -638,11 +527,8 @@ SyntheticDefinition<?, ?, ?> member, DexProgramClass externalSyntheticClass, MainDexInfo mainDexInfo, - Set<DexType> derivedMainDexTypes, Function<DexType, DexProgramClass> definitions) { - member - .getContext() - .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexInfo, derivedMainDexTypes); + member.getContext().addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexInfo); // TODO(b/168584485): Remove this once class-mapping support is removed. DexProgramClass from = definitions.apply(member.getContext().getSynthesizingContextType()); if (from != null) { @@ -655,7 +541,6 @@ // This is currently also disabled on CF to CF desugaring to avoid missing class references to // the annotated classes. // TODO(b/147485959): Find an alternative encoding for synthetics to avoid missing-class refs. - // TODO(b/168584485): Remove support for main-dex tracing with the class-map annotation. return options.intermediate && !options.cfToCfDesugar; } @@ -670,10 +555,12 @@ potentialEquivalences.forEach( members -> { List<List<T>> groups = - groupEquivalent(members, intermediate, appView.graphLens(), classToFeatureSplitMap); + groupEquivalent( + members, intermediate, appView.graphLens(), classToFeatureSplitMap, synthetics); for (List<T> group : groups) { T representative = - findDeterministicRepresentative(group, appView.graphLens(), classToFeatureSplitMap); + findDeterministicRepresentative( + group, appView.graphLens(), classToFeatureSplitMap, synthetics); // The representative is required to be the first element of the group. group.remove(representative); group.add(0, representative); @@ -691,7 +578,8 @@ // representative which is equal to 'context' here (see assert below). groups.sort( (a, b) -> - a.compareToIncludingContext(b, appView.graphLens(), classToFeatureSplitMap)); + a.compareToIncludingContext( + b, appView.graphLens(), classToFeatureSplitMap, synthetics)); for (int i = 0; i < groups.size(); i++) { EquivalenceGroup<T> group = groups.get(i); assert group @@ -702,7 +590,11 @@ // of the synthetic name will be non-deterministic between the two. assert i == 0 || checkGroupsAreDistinct( - groups.get(i - 1), group, appView.graphLens(), classToFeatureSplitMap); + groups.get(i - 1), + group, + appView.graphLens(), + classToFeatureSplitMap, + synthetics); SyntheticKind kind = group.members.get(0).getKind(); DexType representativeType = createExternalType(kind, externalSyntheticTypePrefix, generators, appView); @@ -716,14 +608,15 @@ List<T> potentialEquivalence, boolean intermediate, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { List<List<T>> groups = new ArrayList<>(); // Each other member is in a shared group if it is actually equivalent to the first member. for (T synthetic : potentialEquivalence) { boolean requireNewGroup = true; for (List<T> group : groups) { if (synthetic.isEquivalentTo( - group.get(0), intermediate, graphLens, classToFeatureSplitMap)) { + group.get(0), intermediate, graphLens, classToFeatureSplitMap, syntheticItems)) { requireNewGroup = false; group.add(synthetic); break; @@ -742,15 +635,20 @@ EquivalenceGroup<T> g1, EquivalenceGroup<T> g2, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { - int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap); + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { + int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap, syntheticItems); assert order != 0; - assert order != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap); + assert order + != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap, syntheticItems); return true; } private static <T extends SyntheticDefinition<?, T, ?>> T findDeterministicRepresentative( - List<T> members, GraphLens graphLens, ClassToFeatureSplitMap classToFeatureSplitMap) { + List<T> members, + GraphLens graphLens, + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { // Pick a deterministic member as representative. T smallest = members.get(0); for (int i = 1; i < members.size(); i++) { @@ -794,7 +692,8 @@ boolean intermediate, DexItemFactory factory, GraphLens graphLens, - ClassToFeatureSplitMap classToFeatureSplitMap) { + ClassToFeatureSplitMap classToFeatureSplitMap, + SyntheticItems syntheticItems) { if (definitions.isEmpty()) { return Collections.emptyList(); } @@ -817,7 +716,8 @@ RepresentativeMap map = t -> syntheticTypes.contains(t) ? factory.voidType : t; Map<HashCode, List<T>> equivalences = new HashMap<>(definitions.size()); for (T definition : definitions.values()) { - HashCode hash = definition.computeHash(map, intermediate, classToFeatureSplitMap); + HashCode hash = + definition.computeHash(map, intermediate, classToFeatureSplitMap, syntheticItems); equivalences.computeIfAbsent(hash, k -> new ArrayList<>()).add(definition); } return equivalences.values();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java index 8263f40..9922011 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -84,6 +84,11 @@ private final PendingSynthetics pending = new PendingSynthetics(); + // Empty collection for use only in tests and utilities. + public static SyntheticItems empty() { + return new SyntheticItems(-1, CommittedSyntheticsCollection.empty()); + } + // Only for use from initial AppInfo/AppInfoWithClassHierarchy create functions. */ public static CommittedItems createInitialSyntheticItems(DexApplication application) { return new CommittedItems( @@ -215,6 +220,18 @@ return isSyntheticClass(clazz.type); } + public Collection<DexType> getSynthesizingContexts(DexType type) { + SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(type); + if (reference != null) { + return Collections.singletonList(reference.getContext().getSynthesizingContextType()); + } + SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(type); + if (definition != null) { + return Collections.singletonList(definition.getContext().getSynthesizingContextType()); + } + return Collections.emptyList(); + } + // TODO(b/180091213): Implement this and remove client provided the oracle. public Set<DexReference> getSynthesizingContexts( DexProgramClass clazz, SynthesizingContextOracle oracle) { @@ -445,6 +462,7 @@ Result computeFinalSynthetics(AppView<?> appView) { assert !hasPendingSyntheticClasses(); - return new SyntheticFinalization(appView.options(), committed).computeFinalSynthetics(appView); + return new SyntheticFinalization(appView.options(), this, committed) + .computeFinalSynthetics(appView); } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java index 25e59ae..1b80f41 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -63,7 +63,7 @@ } } - private static final String SYNTHETIC_CLASS_SEPARATOR = "-$$"; + private static final String SYNTHETIC_CLASS_SEPARATOR = "$$"; /** * The internal synthetic class separator is only used for representing synthetic items during * compilation. In particular, this separator must never be used to write synthetic classes to the
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java index 21c3447..1534b4a 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -42,6 +42,7 @@ import com.android.tools.r8.origin.Origin; import com.android.tools.r8.origin.PathOrigin; import com.android.tools.r8.shaking.FilteredClassPath; +import com.android.tools.r8.synthesis.SyntheticItems; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; @@ -611,7 +612,8 @@ classDescriptor -> { if (featureSplitConfiguration != null) { DexType type = dexItemFactory.createType(classDescriptor); - FeatureSplit featureSplit = classToFeatureSplitMap.getFeatureSplit(type); + FeatureSplit featureSplit = + classToFeatureSplitMap.getFeatureSplit(type, SyntheticItems.empty()); if (featureSplit != null && !featureSplit.isBase()) { return featureSplitArchiveOutputStreams.get(featureSplit); }
diff --git a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java index 76e6f33..15344f9 100644 --- a/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java +++ b/src/main/java/com/android/tools/r8/utils/TypeReferenceUtils.java
@@ -11,6 +11,9 @@ private static final Comparator<TypeReference> COMPARATOR = (type, other) -> { + if (type == other) { + return 0; + } // Handle null inputs (void). if (type == null) { return -1;
diff --git a/src/test/examplesAndroidO/multidex004/ref-list-1.txt b/src/test/examplesAndroidO/multidex004/ref-list-1.txt index c817c33..bd195b3 100644 --- a/src/test/examplesAndroidO/multidex004/ref-list-1.txt +++ b/src/test/examplesAndroidO/multidex004/ref-list-1.txt
@@ -1,4 +1,4 @@ -Lmultidex004/MainActivity-$$ExternalSyntheticLambda0; +Lmultidex004/MainActivity$$ExternalSyntheticLambda0; Lmultidex004/MainActivity; Lmultidex004/VersionInterface; Lmultidex004/VersionStatic;
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java index 82cfebf..43bbd99 100644 --- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -361,15 +361,25 @@ abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass); @Override - protected void testIntermediateWithMainDexList(String packageName, Path input, - int expectedMainDexListSize, String... mainDexClasses) throws Throwable { + protected void testIntermediateWithMainDexList( + String packageName, + Path input, + int expectedMainDexListSize, + List<String> mainDexClasses, + List<String> mainDexOverApproximation) + throws Throwable { // Skip those tests. Assume.assumeTrue(false); } @Override - protected Path buildDexThroughIntermediate(String packageName, Path input, OutputMode outputMode, - AndroidApiLevel minApi, String... mainDexClasses) throws Throwable { + protected Path buildDexThroughIntermediate( + String packageName, + Path input, + OutputMode outputMode, + AndroidApiLevel minApi, + List<String> mainDexClasses) + throws Throwable { // tests using this should already been skipped. throw new Unreachable(); }
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java index 4c88d91..4913bc5 100644 --- a/src/test/java/com/android/tools/r8/D8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.D8Command.Builder; import com.android.tools.r8.TestBase.Backend; import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer; +import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.InternalOptions; @@ -86,4 +87,16 @@ builder.addMainDexRulesFiles(mainDexRuleFiles); return self(); } + + public D8TestBuilder addMainDexRules(String... rules) { + builder.addMainDexRules(Arrays.asList(rules), Origin.unknown()); + return self(); + } + + public D8TestBuilder addMainDexKeepClassRules(Class<?>... classes) { + for (Class<?> clazz : classes) { + addMainDexRules("-keep class " + clazz.getTypeName()); + } + return self(); + } }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java index 2773cf1..52480be 100644 --- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -9,15 +9,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.OffOrAuto; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.StringUtils.BraceType; import com.android.tools.r8.utils.TestDescriptionWatcher; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.FoundClassSubject; @@ -27,6 +31,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; import com.google.common.io.ByteStreams; import java.io.IOException; import java.io.InputStream; @@ -35,13 +41,12 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; @@ -114,6 +119,24 @@ return withBuilderTransformation(builder -> builder.addMainDexClasses(classes)); } + C withMainDexKeepClassRules(List<String> classes) { + return withBuilderTransformation( + builder -> { + if (builder instanceof D8Command.Builder) { + ((D8Command.Builder) builder) + .addMainDexRules( + ListUtils.map(classes, c -> "-keep class " + c), Origin.unknown()); + } else if (builder instanceof R8Command.Builder) { + ((R8Command.Builder) builder) + .addMainDexRules( + ListUtils.map(classes, c -> "-keep class " + c), Origin.unknown()); + } else { + fail("Unexpected builder type: " + builder.getClass()); + } + return builder; + }); + } + C withInterfaceMethodDesugaring(OffOrAuto behavior) { return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior); } @@ -465,15 +488,20 @@ testIntermediateWithMainDexList( "lambdadesugaring", 1, - "lambdadesugaring.LambdaDesugaring$I"); + ImmutableList.of("lambdadesugaring.LambdaDesugaring$I"), + ImmutableList.of()); } @Test public void testLambdaDesugaringWithMainDexList2() throws Throwable { // Main dex class has many lambdas. - testIntermediateWithMainDexList("lambdadesugaring", - 33, - "lambdadesugaring.LambdaDesugaring$Refs$B"); + testIntermediateWithMainDexList( + "lambdadesugaring", + // TODO(b/180074885): Over approximation not present in R8. + this instanceof R8RunExamplesAndroidOTest ? 51 : 52, + ImmutableList.of("lambdadesugaring.LambdaDesugaring$Refs$B"), + // TODO(b/180074885): Over approximation due to invoke-dynamic reference adds as dependency. + ImmutableList.of("lambdadesugaring.LambdaDesugaring$Refs$D")); } @Test @@ -483,7 +511,11 @@ "interfacemethods", Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION), 2, - "interfacemethods.I1"); + ImmutableList.of("interfacemethods.I1"), + // TODO(b/180074885): Over approximation due to including I1-CC by being derived from I1, + // but after desugaring I1 does not reference I1$-CC (the static method is moved), so it + // is incorrect to include I1-CC in the main dex. + ImmutableList.of("interfacemethods.I1$-CC")); } @@ -494,7 +526,11 @@ "interfacemethods", Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION), 2, - "interfacemethods.I2"); + ImmutableList.of("interfacemethods.I2"), + // TODO(b/180074885): Over approximation due to including I2$-CC by being derived from I2, + // but after desugaring I2 does not reference I2$-CC (the default method is moved), so it + // is incorrect to include I2$-CC in the main dex. + ImmutableList.of("interfacemethods.I2$-CC")); } @Test @@ -510,20 +546,23 @@ private void testIntermediateWithMainDexList( String packageName, int expectedMainDexListSize, - String... mainDexClasses) + List<String> mainDexClasses, + List<String> mainDexOverApproximation) throws Throwable { testIntermediateWithMainDexList( packageName, Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION), expectedMainDexListSize, - mainDexClasses); + mainDexClasses, + mainDexOverApproximation); } protected void testIntermediateWithMainDexList( String packageName, Path input, int expectedMainDexListSize, - String... mainDexClasses) + List<String> mainDexClasses, + List<String> mainDexOverApproximation) throws Throwable { AndroidApiLevel minApi = AndroidApiLevel.K; @@ -534,16 +573,18 @@ .withMinApiLevel(minApi) .withOptionConsumer(option -> option.minimalMainDex = true) .withOptionConsumer(option -> option.enableInheritanceClassInDexDistributor = false) - .withMainDexClass(mainDexClasses) + .withMainDexKeepClassRules(mainDexClasses) .withKeepAll(); Path fullDexes = temp.getRoot().toPath().resolve(packageName + "full" + ZIP_EXTENSION); full.build(input, fullDexes); // Builds with intermediate in both output mode. - Path dexesThroughIndexedIntermediate = buildDexThroughIntermediate( - packageName, input, OutputMode.DexIndexed, minApi, mainDexClasses); - Path dexesThroughFilePerInputClassIntermediate = buildDexThroughIntermediate( - packageName, input, OutputMode.DexFilePerClassFile, minApi, mainDexClasses); + Path dexesThroughIndexedIntermediate = + buildDexThroughIntermediate( + packageName, input, OutputMode.DexIndexed, minApi, mainDexClasses); + Path dexesThroughFilePerInputClassIntermediate = + buildDexThroughIntermediate( + packageName, input, OutputMode.DexFilePerClassFile, minApi, mainDexClasses); // Collect main dex types. CodeInspector fullInspector = getMainDexInspector(fullDexes); @@ -551,20 +592,45 @@ getMainDexInspector(dexesThroughIndexedIntermediate); CodeInspector filePerInputClassIntermediateInspector = getMainDexInspector(dexesThroughFilePerInputClassIntermediate); - Collection<String> fullMainClasses = new HashSet<>(); + Set<String> fullMainClasses = new HashSet<>(); fullInspector.forAllClasses( clazz -> fullMainClasses.add(clazz.getFinalDescriptor())); - Collection<String> indexedIntermediateMainClasses = new HashSet<>(); + Set<String> indexedIntermediateMainClasses = new HashSet<>(); indexedIntermediateInspector.forAllClasses( clazz -> indexedIntermediateMainClasses.add(clazz.getFinalDescriptor())); - Collection<String> filePerInputClassIntermediateMainClasses = new HashSet<>(); + Set<String> filePerInputClassIntermediateMainClasses = new HashSet<>(); filePerInputClassIntermediateInspector.forAllClasses( clazz -> filePerInputClassIntermediateMainClasses.add(clazz.getFinalDescriptor())); // Check. Assert.assertEquals(expectedMainDexListSize, fullMainClasses.size()); - Assert.assertEquals(fullMainClasses, indexedIntermediateMainClasses); - Assert.assertEquals(fullMainClasses, filePerInputClassIntermediateMainClasses); + SetView<String> adjustedFull = + Sets.difference( + fullMainClasses, + new HashSet<>( + ListUtils.map(mainDexOverApproximation, DescriptorUtils::javaTypeToDescriptor))); + assertEqualSets(adjustedFull, indexedIntermediateMainClasses); + assertEqualSets(adjustedFull, filePerInputClassIntermediateMainClasses); + } + + <T> void assertEqualSets(Set<T> expected, Set<T> actual) { + SetView<T> missing = Sets.difference(expected, actual); + SetView<T> unexpected = Sets.difference(actual, expected); + if (missing.isEmpty() && unexpected.isEmpty()) { + return; + } + StringBuilder builder = new StringBuilder("Sets differ."); + if (!missing.isEmpty()) { + builder.append("\nMissing items: [\n "); + StringUtils.append(builder, missing, "\n ", BraceType.NONE); + builder.append("\n]"); + } + if (!unexpected.isEmpty()) { + builder.append("\nUnexpected items: [\n "); + StringUtils.append(builder, unexpected, "\n ", BraceType.NONE); + builder.append("\n]"); + } + fail(builder.toString()); } protected Path buildDexThroughIntermediate( @@ -572,7 +638,7 @@ Path input, OutputMode outputMode, AndroidApiLevel minApi, - String... mainDexClasses) + List<String> mainDexClasses) throws Throwable { Path intermediateDex = temp.getRoot().toPath().resolve(packageName + "intermediate" + ZIP_EXTENSION); @@ -591,7 +657,7 @@ test(packageName + "dex", packageName, "N/A") .withOptionConsumer(option -> option.minimalMainDex = true) .withOptionConsumer(option -> option.enableInheritanceClassInDexDistributor = false) - .withMainDexClass(mainDexClasses) + .withMainDexKeepClassRules(mainDexClasses) .withMinApiLevel(minApi) .withKeepAll(); @@ -644,8 +710,7 @@ } } - protected CodeInspector getMainDexInspector(Path zip) - throws IOException, ExecutionException { + protected CodeInspector getMainDexInspector(Path zip) throws IOException { try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) { try (InputStream in = zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontallyMergedClassInliningTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontallyMergedClassInliningTest.java new file mode 100644 index 0000000..0ca2179 --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontallyMergedClassInliningTest.java
@@ -0,0 +1,86 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.classmerging.horizontal; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class HorizontallyMergedClassInliningTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public HorizontallyMergedClassInliningTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addHorizontallyMergedClassesInspector( + inspector -> inspector.assertIsCompleteMergeGroup(A.class, B.class, C.class)) + .allowAccessModification() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + assertThat(inspector.clazz(A.class), isAbsent()); + assertThat(inspector.clazz(B.class), isAbsent()); + assertThat(inspector.clazz(C.class), isAbsent()); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput("Hello world!"); + } + + static class Main { + + public static void main(String[] args) { + print(new A().build()); + print(new B().build()); + print(new C().build()); + } + + static void print(String[] strings) { + for (String string : strings) { + System.out.print(string); + } + } + } + + static class A { + + String[] build() { + return new String[] {"H", "e", "l", "l"}; + } + } + + static class B { + + String[] build() { + return new String[] {"o", " ", "w", "o"}; + } + } + + static class C { + + String[] build() { + return new String[] {"r", "l", "d", "!"}; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java index 05f84af..81f0674 100644 --- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java +++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -153,7 +153,7 @@ testForD8(parameters.getBackend()) .addProgramClasses(CLASSES) .setMinApi(parameters.getApiLevel()) - .addMainDexListClasses(MiniAssert.class, TestClass.class, User2.class) + .addMainDexRules(keepMainProguardConfiguration(TestClass.class)) .setProgramConsumer(mainDexConsumer) .compile() .inspect(this::checkExpectedSynthetics) @@ -185,44 +185,8 @@ testForD8() .addProgramFiles(perClassOutput) .setMinApi(parameters.getApiLevel()) - .addMainDexListClasses(MiniAssert.class, TestClass.class, User2.class) - .setProgramConsumer(mainDexConsumer) - .compile() - .inspect(this::checkExpectedSynthetics) - .run(parameters.getRuntime(), TestClass.class, getRunArgs()) - .assertSuccessWithOutput(EXPECTED); - checkMainDex(mainDexConsumer); - } - - // TODO(b/168584485): This test should be removed once support is dropped. - @Test - public void testD8MergingWithTraceCf() throws Exception { - assumeTrue(parameters.isDexRuntime()); - Path out1 = - testForD8() - .addProgramClasses(User1.class) - .addClasspathClasses(CLASSES) - .setIntermediate(true) - .setMinApi(parameters.getApiLevel()) - .compile() - .writeToZip(); - - Path out2 = - testForD8() - .addProgramClasses(User2.class) - .addClasspathClasses(CLASSES) - .setIntermediate(true) - .setMinApi(parameters.getApiLevel()) - .compile() - .writeToZip(); - - MainDexConsumer mainDexConsumer = new MainDexConsumer(); - testForD8(parameters.getBackend()) - .addProgramClasses(TestClass.class, MiniAssert.class) - .addProgramFiles(out1, out2) - .setMinApi(parameters.getApiLevel()) - .addMainDexListClassReferences( - traceMainDex(CLASSES, Collections.emptyList()).getMainDexList()) + // Trace the classes run by main which will pick up their dependencies. + .addMainDexRules(keepMainProguardConfiguration(TestClass.class)) .setProgramConsumer(mainDexConsumer) .compile() .inspect(this::checkExpectedSynthetics)
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java index af73519..20c2b51 100644 --- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java +++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; +import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.ThrowingConsumer; @@ -55,7 +56,6 @@ r8FullTestBuilder .noMinification() .enableInliningAnnotations() - .addInliningAnnotations() .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O)); ThrowingConsumer<R8TestCompileResult, Exception> ensureLambdaNotInBase = r8TestCompileResult -> { @@ -87,6 +87,10 @@ .addFeatureSplit(FeatureClass.class) .addFeatureSplit(Feature2Class.class) .addKeepFeatureMainRules(BaseSuperClass.class, FeatureClass.class, Feature2Class.class) + .addKeepMethodRules( + Reference.methodFromMethod( + BaseSuperClass.class.getDeclaredMethod( + "keptApplyLambda", MyFunction.class, String.class))) .noMinification() .enableInliningAnnotations() .setMinApi(parameters.getApiLevel()) @@ -108,6 +112,10 @@ } public abstract String getFromFeature(); + + public String keptApplyLambda(MyFunction fn, String arg) { + return fn.apply(arg); + } } public interface MyFunction { @@ -133,7 +141,7 @@ @NeverInline private String useTheLambda(MyFunction f) { - return f.apply("42"); + return keptApplyLambda(f, "42"); } } @@ -159,7 +167,7 @@ @NeverInline private String useTheLambda(MyFunction f) { - return f.apply("43"); + return keptApplyLambda(f, "43"); } } }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java index 1bbcb9e..d3d99ee 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -116,12 +116,11 @@ @Test public void testD8DesugaredLambdasInMainDexList() throws Exception { - Path mainDexList = writeTextToTempFile(testClassMainDexName); TestMainDexListConsumer consumer = new TestMainDexListConsumer(); testForD8() .setMinApi(AndroidApiLevel.K) .addProgramClasses(ImmutableList.of(TestClass.class, MyConsumer.class)) - .addMainDexListFiles(ImmutableList.of(mainDexList)) + .addMainDexListClasses(TestClass.class) .setMainDexListConsumer(consumer) .compile(); assertTrue(consumer.called); @@ -129,7 +128,6 @@ @Test public void testD8DesugaredLambdasInMainDexListMerging() throws Exception { - Path mainDexList = writeTextToTempFile(testClassMainDexName); // Build intermediate dex code first. Path dexOutput = testForD8() @@ -143,7 +141,7 @@ testForD8() .setMinApi(AndroidApiLevel.K) .addProgramFiles(dexOutput) - .addMainDexListFiles(ImmutableList.of(mainDexList)) + .addMainDexKeepClassRules(TestClass.class) .setMainDexListConsumer(consumer) .compile(); assertTrue(consumer.called);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java index 9fa7fe9..5e88d7d 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -72,7 +72,7 @@ D8TestCompileResult compileResult = testForD8() .addProgramFiles(intermediateResult.writeToZip()) - .addMainDexListClasses(TestClass.class, A.class) + .addMainDexKeepClassRules(TestClass.class, A.class) .setMinApiThreshold(parameters.getApiLevel()) .compile(); checkCompilationResult(compileResult);
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java index a203729..4eb6d15 100644 --- a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java +++ b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.StringUtils; -import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.google.common.collect.ImmutableList; import java.util.List; import org.junit.Test; @@ -37,12 +36,16 @@ private static final List<Class<?>> FIRST_CLASSES = ImmutableList.of(FIRST_FOO, FIRST_PKG_PRIVATE); + private static final Class<?> FIRST_FIRST_FOO = + com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first.Foo + .class; + + private static final Class<?> FIRST_FIRST_PKG_PRIVATE = + com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first + .PkgProtectedMethod.class; + private static final List<Class<?>> FIRST_FIRST_CLASSES = - ImmutableList.of( - com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first.Foo - .class, - com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first - .PkgProtectedMethod.class); + ImmutableList.of(FIRST_FIRST_FOO, FIRST_FIRST_PKG_PRIVATE); private static List<Class<?>> getTestClasses() { return ImmutableList.<Class<?>>builder() @@ -84,11 +87,7 @@ .addProgramClasses(getTestClasses()) .addFeatureSplit(getFeatureClasses().toArray(new Class<?>[0])) .addKeepMainRule(TestClass.class) - .addKeepClassAndMembersRules( - com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first - .PkgProtectedMethod.class, - com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first - .first.PkgProtectedMethod.class) + .addKeepClassAndMembersRules(FIRST_PKG_PRIVATE, FIRST_FIRST_PKG_PRIVATE) .addKeepClassAndMembersRules(I.class) .addKeepMethodRules( Reference.methodFromMethod(TestClass.class.getDeclaredMethod("run", I.class))) @@ -106,20 +105,21 @@ // Check that the first.Foo is repackaged but that the pkg private access class is not. // The implies that the lambda which is doing a package private access cannot be repackaged. // If it is, the access will fail resulting in a runtime exception. - CodeInspector baseInspector = compileResult.inspector(); - assertThat(FIRST_FOO, isRepackagedAsExpected(baseInspector, "first")); - assertThat(FIRST_PKG_PRIVATE, isNotRepackaged(baseInspector)); - assertEquals( - getTestClasses().size() + expectedSyntheticsInBase, baseInspector.allClasses().size()); - - // The feature first.first.Foo is not repackaged and neither is the lambda. - // TODO(b/180086194): first.first.Foo should have been repackaged, but is currently identified - // as being in 'base' when inlining takes place. - CodeInspector featureInspector = new CodeInspector(compileResult.getFeature(0)); - getFeatureClasses().forEach(c -> assertThat(c, isNotRepackaged(featureInspector))); - assertEquals( - getFeatureClasses().size() + expectedSyntheticsInFeature, - featureInspector.allClasses().size()); + compileResult.inspect( + baseInspector -> { + assertThat(FIRST_FOO, isRepackagedAsExpected(baseInspector, "first")); + assertThat(FIRST_PKG_PRIVATE, isNotRepackaged(baseInspector)); + assertEquals( + getTestClasses().size() + expectedSyntheticsInBase, + baseInspector.allClasses().size()); + }, + featureInspector -> { + assertThat(FIRST_FIRST_FOO, isRepackagedAsExpected(featureInspector, "first$1")); + assertThat(FIRST_FIRST_PKG_PRIVATE, isNotRepackaged(featureInspector)); + assertEquals( + getFeatureClasses().size() + expectedSyntheticsInFeature, + featureInspector.allClasses().size()); + }); compileResult .addFeatureSplitsToRunClasspathFiles()
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java index a5c623c..91fb002 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -133,6 +133,11 @@ return this; } + public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(Class<?>... classes) { + return assertIsCompleteMergeGroup( + Stream.of(classes).map(Reference::classFromClass).collect(Collectors.toList())); + } + public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(String... typeNames) { return assertIsCompleteMergeGroup( Stream.of(typeNames).map(Reference::classFromTypeName).collect(Collectors.toList()));