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()));