Merge commit 'e80cf5909fb4d2ab373e95bfe57cb36aa605d873' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java index 7095940..ada1c8a 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -346,6 +346,7 @@ this.genericSignature = genericSignature; } + @Override public void clearGenericSignature() { this.genericSignature = FieldTypeSignature.noSignature(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java index f596146..e1bb25e 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -28,6 +28,8 @@ public abstract void clearKotlinInfo(); + public abstract void clearGenericSignature(); + public DexType getHolderType() { return getReference().getHolderType(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 9051374..434b091 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -650,10 +650,6 @@ this.kotlinMemberInfo = kotlinMemberInfo; } - public void clearKotlinMemberInfo() { - kotlinMemberInfo = getNoKotlinInfo(); - } - public boolean isKotlinFunction() { return kotlinMemberInfo.isFunction(); } @@ -1504,6 +1500,7 @@ this.genericSignature = genericSignature; } + @Override public void clearGenericSignature() { this.genericSignature = MethodTypeSignature.noSignature(); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java index 315b8a1..6be5551 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -472,10 +472,14 @@ public void setKotlinInfo(KotlinClassLevelInfo kotlinInfo) { assert kotlinInfo != null; - assert this.kotlinInfo == getNoKotlinInfo() || kotlinInfo == getNoKotlinInfo(); + assert this.kotlinInfo == getNoKotlinInfo(); this.kotlinInfo = kotlinInfo; } + public void clearKotlinInfo() { + this.kotlinInfo = getNoKotlinInfo(); + } + @Override boolean internalClassOrInterfaceMayHaveInitializationSideEffects( AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java index 1a05232..00534ba 100644 --- a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java +++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
@@ -269,16 +269,13 @@ if (enclosingClass == null || enclosedClass == null) { return true; } - if (enclosingReference.isDexMethod()) { - return enclosedClass.getEnclosingMethodAttribute() == null - || enclosedClass.getEnclosingMethodAttribute().getEnclosingMethod() != enclosingReference; + if (enclosedClass.getEnclosingMethodAttribute() != null) { + return enclosingReference.isDexMethod() + ? enclosedClass.getEnclosingMethodAttribute().getEnclosingMethod() != enclosingReference + : enclosedClass.getEnclosingMethodAttribute().getEnclosingClass() != enclosingReference; } else { InnerClassAttribute innerClassAttribute = enclosedClass.getInnerClassAttributeForThisClass(); - if (innerClassAttribute != null) { - return innerClassAttribute.getOuter() != enclosingReference; - } - return enclosedClass.getEnclosingMethodAttribute() == null - || enclosedClass.getEnclosingMethodAttribute().getEnclosingClass() != enclosingReference; + return innerClassAttribute == null || innerClassAttribute.getOuter() != enclosingReference; } }
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java index 7dcfb37..ec86d3e 100644 --- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java +++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -104,13 +104,11 @@ if (appView.options().disableGenericSignatureValidation) { return VALID; } + SignatureEvaluationResult evaluationResult = VALID; for (DexProgramClass clazz : programClasses) { - SignatureEvaluationResult evaluationResult = evaluateSignaturesForClass(clazz); - if (evaluationResult.isInvalid()) { - return evaluationResult; - } + evaluationResult = evaluationResult.combine(evaluateSignaturesForClass(clazz)); } - return VALID; + return evaluationResult; } public SignatureEvaluationResult evaluateSignaturesForClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java index f4aa43b..5a58189 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -101,7 +101,7 @@ @Override public void clearKotlinInfo() { - getDefinition().clearKotlinMemberInfo(); + getDefinition().clearKotlinInfo(); } public MethodPosition getPosition() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java index dfd4f77..89e9f74 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -17,7 +17,6 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.FieldAccessFlags; @@ -41,7 +40,6 @@ import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -72,28 +70,22 @@ private final ClassStaticFieldsMerger classStaticFieldsMerger; private final ClassInstanceFieldsMerger classInstanceFieldsMerger; private final Collection<VirtualMethodMerger> virtualMethodMergers; - private final Collection<InstanceInitializerMerger> instanceInitializerMergers; private ClassMerger( AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode, HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group, - Collection<VirtualMethodMerger> virtualMethodMergers, - Collection<InstanceInitializerMerger> instanceInitializerMergers, - ClassInitializerMerger classInitializerMerger) { + Collection<VirtualMethodMerger> virtualMethodMergers) { this.appView = appView; - this.mode = mode; - this.lensBuilder = lensBuilder; - this.group = group; - this.virtualMethodMergers = virtualMethodMergers; - this.instanceInitializerMergers = instanceInitializerMergers; - this.dexItemFactory = appView.dexItemFactory(); - this.classInitializerMerger = classInitializerMerger; + this.group = group; + this.lensBuilder = lensBuilder; + this.mode = mode; + this.virtualMethodMergers = virtualMethodMergers; + this.classInitializerMerger = ClassInitializerMerger.create(group); this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group); this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(appView, lensBuilder, group); - buildClassIdentifierMap(); } @@ -105,10 +97,9 @@ void mergeDirectMethods( SyntheticArgumentClass syntheticArgumentClass, SyntheticClassInitializerConverter.Builder syntheticClassInitializerConverterBuilder) { + mergeInstanceInitializers(syntheticArgumentClass); mergeStaticClassInitializers(syntheticClassInitializerConverterBuilder); - mergeDirectMethods(group.getTarget()); - group.forEachSource(this::mergeDirectMethods); - mergeConstructors(syntheticArgumentClass); + group.forEach(this::mergeDirectMethods); } void mergeStaticClassInitializers( @@ -183,13 +174,23 @@ classMethodsBuilder::isFresh); } - void mergeConstructors(SyntheticArgumentClass syntheticArgumentClass) { + void mergeInstanceInitializers(SyntheticArgumentClass syntheticArgumentClass) { + InstanceInitializerMergerCollection instanceInitializerMergers = + InstanceInitializerMergerCollection.create(appView, group, lensBuilder, mode); instanceInitializerMergers.forEach( merger -> merger.merge( classMethodsBuilder, lensBuilder, classIdentifiers, syntheticArgumentClass)); } + void mergeMethods( + SyntheticArgumentClass syntheticArgumentClass, + SyntheticClassInitializerConverter.Builder syntheticClassInitializerConverterBuilder) { + mergeVirtualMethods(); + mergeDirectMethods(syntheticArgumentClass, syntheticClassInitializerConverterBuilder); + classMethodsBuilder.setClassMethods(group.getTarget()); + } + void mergeVirtualMethods() { virtualMethodMergers.forEach( merger -> merger.merge(classMethodsBuilder, lensBuilder, classIdentifiers)); @@ -225,12 +226,6 @@ classInstanceFieldsMerger.setClassIdField(classIdField); } - void mergeStaticFields() { - group.forEachSource(classStaticFieldsMerger::addFields); - classStaticFieldsMerger.merge(group.getTarget()); - group.forEachSource(clazz -> clazz.setStaticFields(null)); - } - void fixAccessFlags() { if (Iterables.any(group.getSources(), not(DexProgramClass::isAbstract))) { group.getTarget().getAccessFlags().demoteFromAbstract(); @@ -291,6 +286,14 @@ group.getTarget().setInterfaces(DexTypeList.create(interfaces)); } + void mergeFields() { + if (group.hasClassIdField()) { + appendClassIdField(); + } + mergeInstanceFields(); + mergeStaticFields(); + } + void mergeInstanceFields() { group.forEachSource( clazz -> { @@ -300,25 +303,21 @@ group.getTarget().setInstanceFields(classInstanceFieldsMerger.merge()); } + void mergeStaticFields() { + group.forEachSource(classStaticFieldsMerger::addFields); + classStaticFieldsMerger.merge(group.getTarget()); + group.forEachSource(clazz -> clazz.setStaticFields(null)); + } + public void mergeGroup( SyntheticArgumentClass syntheticArgumentClass, SyntheticClassInitializerConverter.Builder syntheticClassInitializerConverterBuilder) { fixAccessFlags(); fixNestMemberAttributes(); - - if (group.hasClassIdField()) { - appendClassIdField(); - } - mergeAnnotations(); mergeInterfaces(); - - mergeVirtualMethods(); - mergeDirectMethods(syntheticArgumentClass, syntheticClassInitializerConverterBuilder); - classMethodsBuilder.setClassMethods(group.getTarget()); - - mergeStaticFields(); - mergeInstanceFields(); + mergeFields(); + mergeMethods(syntheticArgumentClass, syntheticClassInitializerConverterBuilder); } public static class Builder { @@ -359,53 +358,6 @@ appView.testing().horizontalClassMergingTarget.apply(appView, candidates, target)); } - private ClassInitializerMerger createClassInitializerMerger() { - ClassInitializerMerger.Builder builder = new ClassInitializerMerger.Builder(); - group.forEach( - clazz -> { - if (clazz.hasClassInitializer()) { - builder.add(clazz.getProgramClassInitializer()); - } - }); - return builder.build(); - } - - private List<InstanceInitializerMerger> createInstanceInitializerMergers() { - List<InstanceInitializerMerger> instanceInitializerMergers = new ArrayList<>(); - if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) { - Map<DexProto, InstanceInitializerMerger.Builder> buildersByProto = new LinkedHashMap<>(); - group.forEach( - clazz -> - clazz.forEachProgramDirectMethodMatching( - DexEncodedMethod::isInstanceInitializer, - method -> - buildersByProto - .computeIfAbsent( - method.getDefinition().getProto(), - ignore -> new InstanceInitializerMerger.Builder(appView, mode)) - .add(method))); - for (InstanceInitializerMerger.Builder builder : buildersByProto.values()) { - instanceInitializerMergers.addAll(builder.build(group)); - } - } else { - group.forEach( - clazz -> - clazz.forEachProgramDirectMethodMatching( - DexEncodedMethod::isInstanceInitializer, - method -> - instanceInitializerMergers.addAll( - new InstanceInitializerMerger.Builder(appView, mode) - .add(method) - .build(group)))); - } - - // Try and merge the constructors with the most arguments first, to avoid using synthetic - // arguments if possible. - instanceInitializerMergers.sort( - Comparator.comparing(InstanceInitializerMerger::getArity).reversed()); - return instanceInitializerMergers; - } - private List<VirtualMethodMerger> createVirtualMethodMergers() { Map<DexMethodSignature, VirtualMethodMerger.Builder> virtualMethodMergerBuilders = new LinkedHashMap<>(); @@ -448,14 +400,7 @@ createClassIdField(); } - return new ClassMerger( - appView, - mode, - lensBuilder, - group, - virtualMethodMergers, - createInstanceInitializerMergers(), - createClassInitializerMerger()); + return new ClassMerger(appView, mode, lensBuilder, group, virtualMethodMergers); } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java new file mode 100644 index 0000000..febdbe6 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -0,0 +1,17 @@ +// 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.horizontalclassmerging; + +import com.android.tools.r8.graph.ProgramMethod; + +public class InstanceInitializerAnalysis { + + public static InstanceInitializerDescription analyze( + ProgramMethod instanceInitializer, HorizontalClassMergerGraphLens.Builder lensBuilder) { + // TODO(b/189296638): Return an InstanceInitializerDescription if the given instance initializer + // is parent constructor call followed by a sequence of instance field puts. + return null; + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java new file mode 100644 index 0000000..41f2c4e --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -0,0 +1,91 @@ +// 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.horizontalclassmerging; + +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A simple abstraction of an instance initializer's code, which allows a parent constructor call + * followed by a sequence of instance-put instructions. + */ +public class InstanceInitializerDescription { + + private final int arity; + private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments; + private final DexMethod parentConstructor; + private final List<InstanceFieldInitializationInfo> parentConstructorArguments; + + InstanceInitializerDescription( + int arity, + Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments, + DexMethod parentConstructor, + List<InstanceFieldInitializationInfo> parentConstructorArguments) { + this.arity = arity; + this.instanceFieldAssignments = instanceFieldAssignments; + this.parentConstructor = parentConstructor; + this.parentConstructorArguments = parentConstructorArguments; + } + + public static Builder builder(ProgramMethod instanceInitializer) { + return new Builder(instanceInitializer); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) { + return false; + } + InstanceInitializerDescription description = (InstanceInitializerDescription) obj; + return arity == description.arity + && instanceFieldAssignments.equals(description.instanceFieldAssignments) + && parentConstructor == description.parentConstructor + && parentConstructorArguments.equals(description.parentConstructorArguments); + } + + @Override + public int hashCode() { + return Objects.hash( + arity, instanceFieldAssignments, parentConstructor, parentConstructorArguments); + } + + public static class Builder { + + private final int arity; + + private Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments = + new LinkedHashMap<>(); + private DexMethod parentConstructor; + private List<InstanceFieldInitializationInfo> parentConstructorArguments; + + Builder(ProgramMethod method) { + this.arity = method.getReference().getArity(); + } + + public void addInstancePut(DexField field, InstanceFieldInitializationInfo value) { + instanceFieldAssignments.put(field, value); + } + + public void addInvokeConstructor( + DexMethod method, List<InstanceFieldInitializationInfo> arguments) { + assert parentConstructor == null; + assert parentConstructorArguments == null; + parentConstructor = method; + parentConstructorArguments = arguments; + } + + public InstanceInitializerDescription build() { + assert parentConstructor != null; + return new InstanceInitializerDescription( + arity, instanceFieldAssignments, parentConstructor, parentConstructorArguments); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java index b1f02a9..c2063f3 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -43,14 +43,25 @@ private final DexItemFactory dexItemFactory; private final MergeGroup group; private final List<ProgramMethod> instanceInitializers; + private final InstanceInitializerDescription instanceInitializerDescription; private final Mode mode; InstanceInitializerMerger( AppView<?> appView, MergeGroup group, List<ProgramMethod> instanceInitializers, Mode mode) { + this(appView, group, instanceInitializers, mode, null); + } + + InstanceInitializerMerger( + AppView<?> appView, + MergeGroup group, + List<ProgramMethod> instanceInitializers, + Mode mode, + InstanceInitializerDescription instanceInitializerDescription) { this.appView = appView; this.dexItemFactory = appView.dexItemFactory(); this.group = group; this.instanceInitializers = instanceInitializers; + this.instanceInitializerDescription = instanceInitializerDescription; this.mode = mode; // Constructors should not be empty and all constructors should have the same prototype. @@ -74,6 +85,14 @@ return instanceInitializers.iterator().next().getReference().getArity(); } + public List<ProgramMethod> getInstanceInitializers() { + return instanceInitializers; + } + + public int size() { + return instanceInitializers.size(); + } + public static class Builder { private final AppView<? extends AppInfoWithClassHierarchy> appView; @@ -107,6 +126,11 @@ return this; } + public Builder addEquivalent(ProgramMethod instanceInitializer) { + ListUtils.last(instanceInitializerGroups).add(instanceInitializer); + return this; + } + public List<InstanceInitializerMerger> build(MergeGroup group) { assert instanceInitializerGroups.stream().noneMatch(List::isEmpty); return ListUtils.map( @@ -114,6 +138,15 @@ instanceInitializers -> new InstanceInitializerMerger(appView, group, instanceInitializers, mode)); } + + public InstanceInitializerMerger buildSingle( + MergeGroup group, InstanceInitializerDescription instanceInitializerDescription) { + assert instanceInitializerGroups.stream().noneMatch(List::isEmpty); + assert instanceInitializerGroups.size() == 1; + List<ProgramMethod> instanceInitializers = ListUtils.first(instanceInitializerGroups); + return new InstanceInitializerMerger( + appView, group, instanceInitializers, mode, instanceInitializerDescription); + } } // Returns true if we can simply use an existing constructor as the new constructor. @@ -198,6 +231,8 @@ HorizontalClassMergerGraphLens.Builder lensBuilder, Reference2IntMap<DexType> classIdentifiers, SyntheticArgumentClass syntheticArgumentClass) { + // TODO(b/189296638): Handle merging of equivalent constructors when + // `instanceInitializerDescription` is set. if (isTrivialMerge(classMethodsBuilder)) { mergeTrivial(classMethodsBuilder, lensBuilder); return;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java new file mode 100644 index 0000000..ed7e099 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -0,0 +1,114 @@ +// 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.horizontalclassmerging; + +import static com.android.tools.r8.utils.MapUtils.ignoreKey; + +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProto; +import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode; +import com.android.tools.r8.horizontalclassmerging.InstanceInitializerMerger.Builder; +import com.android.tools.r8.utils.collections.ProgramMethodSet; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class InstanceInitializerMergerCollection { + + private final List<InstanceInitializerMerger> instanceInitializerMergers; + private final Map<InstanceInitializerDescription, InstanceInitializerMerger> + equivalentInstanceInitializerMergers; + + private InstanceInitializerMergerCollection( + List<InstanceInitializerMerger> instanceInitializerMergers, + Map<InstanceInitializerDescription, InstanceInitializerMerger> + equivalentInstanceInitializerMergers) { + assert equivalentInstanceInitializerMergers.isEmpty(); + this.instanceInitializerMergers = instanceInitializerMergers; + this.equivalentInstanceInitializerMergers = equivalentInstanceInitializerMergers; + } + + public static InstanceInitializerMergerCollection create( + AppView<? extends AppInfoWithClassHierarchy> appView, + MergeGroup group, + HorizontalClassMergerGraphLens.Builder lensBuilder, + Mode mode) { + // Create an instance initializer merger for each group of instance initializers that are + // equivalent. + Map<InstanceInitializerDescription, Builder> buildersByDescription = new LinkedHashMap<>(); + ProgramMethodSet buildersWithoutDescription = ProgramMethodSet.createLinked(); + group.forEach( + clazz -> + clazz.forEachProgramDirectMethodMatching( + DexEncodedMethod::isInstanceInitializer, + instanceInitializer -> { + InstanceInitializerDescription description = + InstanceInitializerAnalysis.analyze(instanceInitializer, lensBuilder); + if (description != null) { + buildersByDescription + .computeIfAbsent( + description, + ignoreKey(() -> new InstanceInitializerMerger.Builder(appView, mode))) + .addEquivalent(instanceInitializer); + } else { + buildersWithoutDescription.add(instanceInitializer); + } + })); + + Map<InstanceInitializerDescription, InstanceInitializerMerger> + equivalentInstanceInitializerMergers = new LinkedHashMap<>(); + buildersByDescription.forEach( + (description, builder) -> { + InstanceInitializerMerger instanceInitializerMerger = + builder.buildSingle(group, description); + if (instanceInitializerMerger.size() == 1) { + // If there is only one constructor with a specific behavior, then consider it for + // normal instance initializer merging below. + buildersWithoutDescription.addAll(instanceInitializerMerger.getInstanceInitializers()); + } else { + equivalentInstanceInitializerMergers.put(description, instanceInitializerMerger); + } + }); + + // Merge instance initializers with different behavior. + List<InstanceInitializerMerger> instanceInitializerMergers = new ArrayList<>(); + if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) { + Map<DexProto, Builder> buildersByProto = new LinkedHashMap<>(); + buildersWithoutDescription.forEach( + instanceInitializer -> + buildersByProto + .computeIfAbsent( + instanceInitializer.getDefinition().getProto(), + ignore -> new InstanceInitializerMerger.Builder(appView, mode)) + .add(instanceInitializer)); + for (InstanceInitializerMerger.Builder builder : buildersByProto.values()) { + instanceInitializerMergers.addAll(builder.build(group)); + } + } else { + buildersWithoutDescription.forEach( + instanceInitializer -> + instanceInitializerMergers.addAll( + new InstanceInitializerMerger.Builder(appView, mode) + .add(instanceInitializer) + .build(group))); + } + + // Try and merge the constructors with the most arguments first, to avoid using synthetic + // arguments if possible. + instanceInitializerMergers.sort( + Comparator.comparing(InstanceInitializerMerger::getArity).reversed()); + return new InstanceInitializerMergerCollection( + instanceInitializerMergers, equivalentInstanceInitializerMergers); + } + + public void forEach(Consumer<InstanceInitializerMerger> consumer) { + instanceInitializerMergers.forEach(consumer); + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java index a720407..11fa11e 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.horizontalclassmerging.MergeGroup; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.IRMetadata; @@ -56,6 +57,17 @@ this.classInitializers = classInitializers; } + public static ClassInitializerMerger create(MergeGroup group) { + ClassInitializerMerger.Builder builder = new ClassInitializerMerger.Builder(); + group.forEach( + clazz -> { + if (clazz.hasClassInitializer()) { + builder.add(clazz.getProgramClassInitializer()); + } + }); + return builder.build(); + } + public boolean isEmpty() { return classInitializers.isEmpty(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java index fc457f7..c53395c 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -95,16 +95,20 @@ if (newGroup != null) { newGroup.add(clazz, superInterfaces, subInterfaces); } else { - newGroupsWithInfo.add(new MergeGroupWithInfo(clazz, superInterfaces, subInterfaces)); + newGroup = new MergeGroupWithInfo(clazz, superInterfaces, subInterfaces); + newGroupsWithInfo.add(newGroup); } + committed.put(clazz, newGroup.getGroup()); } List<MergeGroup> newGroups = new LinkedList<>(); for (MergeGroupWithInfo newGroupWithInfo : newGroupsWithInfo) { MergeGroup newGroup = newGroupWithInfo.getGroup(); - if (!newGroup.isTrivial()) { + if (newGroup.isTrivial()) { + assert !newGroup.isEmpty(); + committed.remove(newGroup.getClasses().getFirst()); + } else { newGroups.add(newGroup); - newGroup.forEach(clazz -> committed.put(clazz, newGroup)); } } return newGroups;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java index ff28c27..5929db5 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -139,15 +139,11 @@ DexProgramClass ensureEmulateInterfaceLibrary( DexProgramClass emulatedInterface, ProgramMethodSet synthesizedMethods) { assert rewriter.isEmulatedInterface(emulatedInterface.type); - DexType emulateLibraryClassType = - InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType( - emulatedInterface.type, appView.dexItemFactory()); DexProgramClass emulateInterfaceClass = appView .getSyntheticItems() - .ensureFixedClassWhileMigrating( + .ensureFixedClass( SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS, - emulateLibraryClassType, emulatedInterface, appView, builder -> @@ -159,6 +155,9 @@ synthesizeEmulatedInterfaceMethod( method, emulatedInterface, methodBuilder)))); emulateInterfaceClass.forEachProgramMethod(synthesizedMethods::add); + assert emulateInterfaceClass.getType() + == InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType( + emulatedInterface.type, appView.dexItemFactory()); return emulateInterfaceClass; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java index 782ff63..bdd6064 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -345,8 +345,8 @@ // behavior. if (cfInvoke.isInterface()) { leavingStaticInvokeToInterface(context); + warnMissingType(context, invokedMethod.holder); } - warnMissingType(context, invokedMethod.holder); return; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java index bd95f96..6ba6863 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -126,9 +126,8 @@ DexProgramClass companionClass = appView .getSyntheticItems() - .ensureFixedClassWhileMigrating( + .ensureFixedClass( SyntheticNaming.SyntheticKind.COMPANION_CLASS, - rewriter.getCompanionClassType(iface.type), iface, appView, builder -> { @@ -139,7 +138,7 @@ processVirtualInterfaceMethods(iface, builder); processDirectInterfaceMethods(iface, builder); }); - + assert companionClass.getType() == rewriter.getCompanionClassType(iface.type); assert companionClass.hasMethods(); // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java index bfabd6c..7f4c47d 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -33,6 +33,8 @@ public final class KotlinClassMetadataReader { + private static final int SYNTHETIC_CLASS_KIND = 3; + public static KotlinClassLevelInfo getKotlinInfo( DexClass clazz, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode) { DexAnnotation meta = @@ -75,16 +77,25 @@ public static boolean isLambda(AppView<?> appView, DexClass clazz) { DexItemFactory dexItemFactory = appView.dexItemFactory(); Kotlin kotlin = dexItemFactory.kotlin; + Flavour flavour = getFlavour(clazz, kotlin); + if (flavour == Flavour.Unclassified) { + return false; + } DexAnnotation metadataAnnotation = clazz.annotations().getFirstMatching(dexItemFactory.kotlinMetadataType); - if (metadataAnnotation != null) { - KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation.annotation); + if (metadataAnnotation == null) { + return false; + } + Map<DexString, DexAnnotationElement> elementMap = toElementMap(metadataAnnotation.annotation); + if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) { + KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap); if (kMetadata instanceof SyntheticClass) { - SyntheticClass syntheticClass = (SyntheticClass) kMetadata; - return syntheticClass.isLambda() - && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified; + return ((SyntheticClass) kMetadata).isLambda(); } } + assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass + == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) + : "Synthetic class kinds should agree"; return false; } @@ -98,16 +109,21 @@ public static KotlinClassMetadata toKotlinClassMetadata( Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) { + return toKotlinClassMetadata(kotlin, toElementMap(metadataAnnotation)); + } + + private static Map<DexString, DexAnnotationElement> toElementMap( + DexEncodedAnnotation metadataAnnotation) { Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>(); for (DexAnnotationElement element : metadataAnnotation.elements) { elementMap.put(element.name, element); } + return elementMap; + } - DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind); - if (kind == null) { - throw new MetadataError("element 'k' is missing."); - } - Integer k = (Integer) kind.value.getBoxedValue(); + private static KotlinClassMetadata toKotlinClassMetadata( + Kotlin kotlin, Map<DexString, DexAnnotationElement> elementMap) { + int k = getKind(kotlin, elementMap); DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion); int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv"); DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion); @@ -127,6 +143,14 @@ return KotlinClassMetadata.read(header); } + private static int getKind(Kotlin kotlin, Map<DexString, DexAnnotationElement> elementMap) { + DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind); + if (kind == null) { + throw new MetadataError("element 'k' is missing."); + } + return (Integer) kind.value.getBoxedValue(); + } + public static KotlinClassLevelInfo createKotlinInfo( Kotlin kotlin, DexClass clazz,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java index fc56e4e..8a4d337 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier; +import com.android.tools.r8.shaking.KeepClassInfo; import com.google.common.collect.Sets; import java.util.Set; @@ -49,28 +50,31 @@ @Override public void done(Enqueuer enqueuer) { - DexType kotlinMetadataType = appView.dexItemFactory().kotlinMetadataType; - DexClass kotlinMetadataClass = - appView.appInfo().definitionForWithoutExistenceAssert(kotlinMetadataType); // In the first round of tree shaking build up all metadata such that it can be traced later. - boolean keepMetadata = - kotlinMetadataClass == null - || kotlinMetadataClass.isNotProgramClass() - || enqueuer.isPinned(kotlinMetadataType); + boolean keepKotlinMetadata = + KeepClassInfo.isKotlinMetadataClassKept( + appView.dexItemFactory(), + appView.appInfo()::definitionForWithoutExistenceAssert, + enqueuer::getKeepInfo); + // In the first round of tree shaking build up all metadata such that it can be traced later. if (enqueuer.getMode().isInitialTreeShaking()) { Set<DexMethod> keepByteCodeFunctions = Sets.newIdentityHashSet(); Set<DexProgramClass> localOrAnonymousClasses = Sets.newIdentityHashSet(); enqueuer.forAllLiveClasses( clazz -> { assert clazz.getKotlinInfo().isNoKotlinInformation(); - if (!keepMetadata || !enqueuer.isPinned(clazz.getType())) { + if (enqueuer + .getKeepInfo(clazz) + .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) { if (KotlinClassMetadataReader.isLambda(appView, clazz) && clazz.hasClassInitializer()) { feedback.classInitializerMayBePostponed(clazz.getClassInitializer()); } - clazz.setKotlinInfo(getNoKotlinInfo()); + clazz.clearKotlinInfo(); clazz.removeAnnotations( - annotation -> annotation.getAnnotationType() == kotlinMetadataType); + annotation -> + annotation.getAnnotationType() + == appView.dexItemFactory().kotlinMetadataType); } else { clazz.setKotlinInfo( KotlinClassMetadataReader.getKotlinInfo( @@ -104,15 +108,20 @@ assert enqueuer.getMode().isFinalTreeShaking(); enqueuer.forAllLiveClasses( clazz -> { - if (!enqueuer.isPinned(clazz.getType())) { - clazz.setKotlinInfo(getNoKotlinInfo()); + if (enqueuer + .getKeepInfo(clazz) + .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) { + clazz.clearKotlinInfo(); clazz.members().forEach(DexEncodedMember::clearKotlinInfo); clazz.removeAnnotations( - annotation -> annotation.getAnnotationType() == kotlinMetadataType); + annotation -> + annotation.getAnnotationType() + == appView.dexItemFactory().kotlinMetadataType); } else { - assert !hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz)) - || !keepMetadata - || clazz.getKotlinInfo() != getNoKotlinInfo(); + // Use the concrete getNoKotlinInfo() instead of isNoKotlinInformation() to handle + // invalid kotlin info as well. + assert hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz)) + == (clazz.getKotlinInfo() != getNoKotlinInfo()); } }); }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java index 08e95b3..ee55f8b 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.utils.Pair; import kotlinx.metadata.KmLambda; import kotlinx.metadata.jvm.KotlinClassHeader; -import kotlinx.metadata.jvm.KotlinClassMetadata; import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass; import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass.Writer; @@ -55,7 +54,7 @@ ? KotlinLambdaInfo.create( clazz, lambda, appView.dexItemFactory(), appView.reporter(), extensionInformation) : null, - getFlavour(syntheticClass, clazz, kotlin), + getFlavour(clazz, kotlin), packageName, metadataVersion); } @@ -64,14 +63,6 @@ return lambda != null && flavour != Flavour.Unclassified; } - public boolean isKotlinStyleLambda() { - return flavour == Flavour.KotlinStyleLambda; - } - - public boolean isJavaStyleLambda() { - return flavour == Flavour.JavaStyleLambda; - } - @Override public boolean isSyntheticClass() { return true; @@ -112,23 +103,17 @@ return metadataVersion; } - public static Flavour getFlavour( - KotlinClassMetadata.SyntheticClass metadata, DexClass clazz, Kotlin kotlin) { - // Returns KotlinStyleLambda if the given clazz is a Kotlin-style lambda: - // a class that - // 1) is recognized as lambda in its Kotlin metadata; - // 2) directly extends kotlin.jvm.internal.Lambda - if (metadata.isLambda() && clazz.superType == kotlin.functional.lambdaType) { + public static Flavour getFlavour(DexClass clazz, Kotlin kotlin) { + // Returns KotlinStyleLambda if the given clazz has shape of a Kotlin-style lambda: + // a class that directly extends kotlin.jvm.internal.Lambda + if (clazz.superType == kotlin.functional.lambdaType) { return Flavour.KotlinStyleLambda; } - // Returns JavaStyleLambda if the given clazz is a Java-style lambda: + // Returns JavaStyleLambda if the given clazz has shape of a Java-style lambda: // a class that - // 1) is recognized as lambda in its Kotlin metadata; - // 2) doesn't extend any other class; - // 3) directly implements only one Java SAM. - if (metadata.isLambda() - && clazz.superType == kotlin.factory.objectType - && clazz.interfaces.size() == 1) { + // 1) doesn't extend any other class; + // 2) directly implements only one Java SAM. + if (clazz.superType == kotlin.factory.objectType && clazz.interfaces.size() == 1) { return Flavour.JavaStyleLambda; } return Flavour.Unclassified;
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 e1eabf2..567b7cb 100644 --- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java +++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexDefinition; import com.android.tools.r8.graph.DexEncodedAnnotation; -import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexEncodedMember; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexProgramClass; @@ -168,11 +168,12 @@ stripAttributes(clazz); clazz.setAnnotations( clazz.annotations().rewrite(annotation -> rewriteAnnotation(clazz, annotation))); - // Kotlin properties are split over fields and methods. Check if any is pinned before pruning - // the information. + // Kotlin metadata for classes are removed in the KotlinMetadataEnqueuerExtension. Kotlin + // properties are split over fields and methods. Check if any is pinned before pruning the + // information. Set<KotlinPropertyInfo> pinnedKotlinProperties = Sets.newIdentityHashSet(); - clazz.forEachMethod(method -> processMethod(method, clazz, pinnedKotlinProperties)); - clazz.forEachField(field -> processField(field, clazz, pinnedKotlinProperties)); + clazz.forEachProgramMember( + member -> processMember(member.getDefinition(), clazz, pinnedKotlinProperties)); clazz.forEachProgramMember( member -> { KotlinMemberLevelInfo kotlinInfo = member.getKotlinInfo(); @@ -182,40 +183,43 @@ } }); } + assert verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo(); } - private void processMethod( - DexEncodedMethod method, - DexProgramClass clazz, - Set<KotlinPropertyInfo> pinnedKotlinProperties) { - method.setAnnotations( - method.annotations().rewrite(annotation -> rewriteAnnotation(method, annotation))); - method.parameterAnnotationsList = - method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations); - KeepMethodInfo methodInfo = appView.getKeepInfo().getMethodInfo(method, clazz); - if (methodInfo.isSignatureAttributeRemovalAllowed(options)) { - method.clearGenericSignature(); + private boolean verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo() { + for (DexProgramClass clazz : appView.appInfo().classes()) { + if (clazz.getKotlinInfo().isNoKotlinInformation()) { + clazz.forEachProgramMember( + member -> { + assert member.getKotlinInfo().isNoKotlinInformation() + : "Should have pruned kotlin info"; + }); + } } - if (!methodInfo.isPinned() && method.getKotlinInfo().isFunction()) { - method.clearKotlinMemberInfo(); - } - if (methodInfo.isPinned() && method.getKotlinInfo().isProperty()) { - pinnedKotlinProperties.add(method.getKotlinInfo().asProperty()); - } + return true; } - private void processField( - DexEncodedField field, + private void processMember( + DexEncodedMember<?, ?> member, DexProgramClass clazz, Set<KotlinPropertyInfo> pinnedKotlinProperties) { - field.setAnnotations( - field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation))); - KeepFieldInfo fieldInfo = appView.getKeepInfo().getFieldInfo(field, clazz); - if (fieldInfo.isSignatureAttributeRemovalAllowed(options)) { - field.clearGenericSignature(); + member.setAnnotations( + member.annotations().rewrite(annotation -> rewriteAnnotation(member, annotation))); + if (member.isDexEncodedMethod()) { + DexEncodedMethod method = member.asDexEncodedMethod(); + method.parameterAnnotationsList = + method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations); } - if (fieldInfo.isPinned() && field.getKotlinInfo().isProperty()) { - pinnedKotlinProperties.add(field.getKotlinInfo().asProperty()); + KeepMemberInfo<?, ?> memberInfo = appView.getKeepInfo().getMemberInfo(member, clazz); + if (memberInfo.isSignatureAttributeRemovalAllowed(options)) { + member.clearGenericSignature(); + } + if (!member.getKotlinInfo().isProperty() && memberInfo.isKotlinMetadataRemovalAllowed(clazz)) { + member.clearKotlinInfo(); + } + // Postpone removal of kotlin property info until we have seen all fields, setters and getters. + if (member.getKotlinInfo().isProperty() && !memberInfo.isKotlinMetadataRemovalAllowed(clazz)) { + pinnedKotlinProperties.add(member.getKotlinInfo().asProperty()); } } @@ -266,20 +270,18 @@ // need to keep the enclosing method and inner classes attributes, if requested. In Proguard // compatibility mode we keep these attributes independent of whether the given class is kept. // In full mode we remove the attribute if not both sides are kept. + KeepClassInfo keepInfo = appView.getKeepInfo().getClassInfo(clazz); clazz.removeEnclosingMethodAttribute( enclosingMethodAttribute -> - appView - .getKeepInfo() - .getClassInfo(clazz) - .isEnclosingMethodAttributeRemovalAllowed( - options, enclosingMethodAttribute, appView)); + keepInfo.isEnclosingMethodAttributeRemovalAllowed( + options, enclosingMethodAttribute, appView)); // It is important that the call to getEnclosingMethodAttribute is done after we potentially // pruned it above. clazz.removeInnerClasses( attribute -> canRemoveInnerClassAttribute(clazz, attribute, clazz.getEnclosingMethodAttribute())); if (clazz.getClassSignature().isValid() - && appView.getKeepInfo().getClassInfo(clazz).isSignatureAttributeRemovalAllowed(options)) { + && keepInfo.isSignatureAttributeRemovalAllowed(options)) { clazz.clearClassSignature(); } }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index 1023e70..61db1bf 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -653,8 +653,8 @@ return clazz; } - public boolean isPinned(DexType type) { - return keepInfo.isPinned(type, appInfo); + public KeepClassInfo getKeepInfo(DexProgramClass clazz) { + return keepInfo.getClassInfo(clazz); } private void addLiveNonProgramType(
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java index cc11777..07f625d 100644 --- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java +++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -21,4 +21,6 @@ boolean isKeepEnclosingMethodAttributeEnabled(); boolean isKeepInnerClassesAttributeEnabled(); + + boolean isKeepRuntimeVisibleAnnotationsEnabled(); }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java index 40b2c9e..a12ca25 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -3,6 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; +import java.util.function.Function; + /** Immutable keep requirements for a class. */ public final class KeepClassInfo extends KeepInfo<KeepClassInfo.Builder, KeepClassInfo> { @@ -41,6 +48,31 @@ && !internalIsAccessModificationRequiredForRepackaging(); } + public boolean isKotlinMetadataRemovalAllowed( + GlobalKeepInfoConfiguration configuration, boolean kotlinMetadataKept) { + return !kotlinMetadataKept + || !isPinned() + || !configuration.isKeepRuntimeVisibleAnnotationsEnabled(); + } + + public static boolean isKotlinMetadataClassKept(AppView<?> appView) { + return isKotlinMetadataClassKept( + appView.dexItemFactory(), + appView.appInfo()::definitionForWithoutExistenceAssert, + appView.getKeepInfo()::getClassInfo); + } + + public static boolean isKotlinMetadataClassKept( + DexItemFactory factory, + Function<DexType, DexClass> definitionForWithoutExistenceAssert, + Function<DexProgramClass, KeepClassInfo> getClassInfo) { + DexType kotlinMetadataType = factory.kotlinMetadataType; + DexClass kotlinMetadataClass = definitionForWithoutExistenceAssert.apply(kotlinMetadataType); + return kotlinMetadataClass == null + || kotlinMetadataClass.isNotProgramClass() + || getClassInfo.apply(kotlinMetadataClass.asProgramClass()).isPinned(); + } + @Override public boolean isTop() { return this.equals(top());
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java index 770e84c..46d8fa2 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.shaking.KeepInfo.Builder; /** Immutable keep requirements for a member. */ @@ -18,4 +19,10 @@ return configuration.isRepackagingEnabled() && !internalIsAccessModificationRequiredForRepackaging(); } + + public boolean isKotlinMetadataRemovalAllowed(DexProgramClass holder) { + // Checking the holder for missing kotlin information relies on the holder being processed + // before members. + return holder.getKotlinInfo().isNoKotlinInformation() || !isPinned(); + } }
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 a07b61b..89552fb 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -566,12 +566,16 @@ groups.get(i - 1), group, appView.graphLens(), classToFeatureSplitMap); SyntheticKind kind = group.getRepresentative().getKind(); DexType representativeType = - createExternalType( - kind, - externalSyntheticTypePrefix, - generators, - appView, - equivalences::containsKey); + intermediate + && synthetics.isSyntheticInput( + group.getRepresentative().getHolder().asProgramClass()) + ? group.getRepresentative().getHolder().getType() + : createExternalType( + kind, + externalSyntheticTypePrefix, + generators, + appView, + equivalences::containsKey); equivalences.put(representativeType, group); } });
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 78c046f..15719ce 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -26,6 +26,7 @@ import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.ListUtils; +import com.android.tools.r8.utils.StringDiagnostic; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.util.ArrayList; @@ -126,6 +127,21 @@ // TODO(b/158159959): Consider populating the input synthetics when identified. for (DexProgramClass clazz : appView.appInfo().classes()) { SyntheticMarker marker = SyntheticMarker.stripMarkerFromClass(clazz, appView); + if (!appView.options().intermediate && marker.getContext() != null) { + DexClass contextClass = + appView + .appInfo() + .definitionForWithoutExistenceAssert( + marker.getContext().getSynthesizingContextType()); + if (contextClass == null || contextClass.isNotProgramClass()) { + appView + .reporter() + .error( + new StringDiagnostic( + "Attempt at compiling intermediate artifact without its context", + clazz.getOrigin())); + } + } if (marker.isSyntheticMethods()) { clazz.forEachProgramMethod( // TODO(b/158159959): Support having multiple methods per class. @@ -439,6 +455,20 @@ return legacyItem; } + private DexProgramClass internalCreateClass( + SyntheticKind kind, + Consumer<SyntheticProgramClassBuilder> fn, + SynthesizingContext outerContext, + DexType type, + DexItemFactory factory) { + SyntheticProgramClassBuilder classBuilder = + new SyntheticProgramClassBuilder(type, outerContext, factory); + fn.accept(classBuilder); + DexProgramClass clazz = classBuilder.build(); + addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz)); + return clazz; + } + public DexProgramClass createClass( SyntheticKind kind, UniqueContext context, @@ -450,12 +480,7 @@ DexType type = SyntheticNaming.createInternalType( kind, outerContext, context.getSyntheticSuffix(), appView.dexItemFactory()); - SyntheticProgramClassBuilder classBuilder = - new SyntheticProgramClassBuilder(type, outerContext, appView.dexItemFactory()); - fn.accept(classBuilder); - DexProgramClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz)); - return clazz; + return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory()); } // TODO(b/172194101): Make this take a unique context. @@ -468,38 +493,42 @@ // This is to ensure a flat input-type -> synthetic-item mapping. SynthesizingContext outerContext = getSynthesizingContext(context, appView); DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); - SyntheticProgramClassBuilder classBuilder = - new SyntheticProgramClassBuilder(type, outerContext, appView.dexItemFactory()); - fn.accept(classBuilder); - DexProgramClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz)); - return clazz; + return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory()); } - // This is a temporary API for migration to the hygienic synthetic, the classes created behave - // like a hygienic synthetic, but use the legacyType passed as parameter instead of the - // hygienic type. - public DexProgramClass ensureFixedClassWhileMigrating( + /** + * Ensure that a fixed synthetic class exists. + * + * <p>This method is thread safe and will synchronize based on the context of the fixed synthetic. + */ + public DexProgramClass ensureFixedClass( SyntheticKind kind, - DexType legacyType, DexProgramClass context, AppView<?> appView, Consumer<SyntheticProgramClassBuilder> fn) { + assert kind.isFixedSuffixSynthetic; + // Obtain the outer synthesizing context in the case the context itself is synthetic. + // This is to ensure a flat input-type -> synthetic-item mapping. + SynthesizingContext outerContext = getSynthesizingContext(context, appView); + DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); + // Fast path is that the synthetic is already present. If so it must be a program class. + DexClass clazz = appView.definitionFor(type, context); + if (clazz != null) { + assert isSyntheticClass(type); + assert clazz.isProgramClass(); + return clazz.asProgramClass(); + } + // Slow path creates the class using the context to make it thread safe. synchronized (context) { - DexClass dexClass = appView.definitionFor(legacyType); - if (dexClass != null) { - assert dexClass.isProgramClass(); - return dexClass.asProgramClass(); + // Recheck if it is present now the lock is held. + clazz = appView.definitionFor(type, context); + if (clazz != null) { + assert isSyntheticClass(type); + assert clazz.isProgramClass(); + return clazz.asProgramClass(); } - // Obtain the outer synthesizing context in the case the context itself is synthetic. - // This is to ensure a flat input-type -> synthetic-item mapping. - SynthesizingContext outerContext = getSynthesizingContext(context, appView); - SyntheticProgramClassBuilder classBuilder = - new SyntheticProgramClassBuilder(legacyType, outerContext, appView.dexItemFactory()); - fn.accept(classBuilder); - DexProgramClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz)); - return clazz; + assert !isSyntheticClass(type); + return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory()); } } @@ -570,12 +599,7 @@ // This is to ensure a flat input-type -> synthetic-item mapping. SynthesizingContext outerContext = SynthesizingContext.fromType(contextType); DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory); - SyntheticProgramClassBuilder classBuilder = - new SyntheticProgramClassBuilder(type, outerContext, factory); - fn.accept(classBuilder); - DexProgramClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz)); - return clazz; + return internalCreateClass(kind, fn, outerContext, type, factory); } /** Create a single synthetic method item. */
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 20f0ab4..6ed6c0e 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -24,8 +24,8 @@ public enum SyntheticKind { // Class synthetics. RECORD_TAG("", 1, false, true, true), - COMPANION_CLASS("-CC", 2, false, true), - EMULATED_INTERFACE_CLASS("-EL", 3, false, true), + COMPANION_CLASS("$-CC", 2, false, true), + EMULATED_INTERFACE_CLASS("$-EL", 3, false, true), LAMBDA("Lambda", 4, false), INIT_TYPE_ARGUMENT("-IA", 5, false, true), HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", 6, false, true),
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 9003eca..e08fe12 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -615,6 +615,11 @@ return proguardConfiguration.getKeepAttributes().innerClasses; } + @Override + public boolean isKeepRuntimeVisibleAnnotationsEnabled() { + return proguardConfiguration.getKeepAttributes().runtimeVisibleAnnotations; + } + /** * If any non-static class merging is enabled, information about types referred to by instanceOf * and check cast instructions needs to be collected. @@ -1218,7 +1223,6 @@ !Version.isDevelopmentVersion() || System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null; private boolean enableInterfaceMergingInInitial = false; - private boolean enableInterfaceMergingInFinal = false; private boolean enableSyntheticMerging = true; private boolean ignoreRuntimeTypeChecksForTesting = false; private boolean restrictToSynthetics = false; @@ -1273,7 +1277,7 @@ return enableInterfaceMergingInInitial; } assert mode.isFinal(); - return enableInterfaceMergingInFinal; + return true; } public boolean isRestrictedToSynthetics() { @@ -1284,10 +1288,6 @@ enableInterfaceMergingInInitial = true; } - public void setEnableInterfaceMergingInFinal() { - enableInterfaceMergingInFinal = true; - } - public void setIgnoreRuntimeTypeChecksForTesting() { ignoreRuntimeTypeChecksForTesting = true; }
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java index dfca4e2..1a9800a 100644 --- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.D8Command.Builder; +import com.android.tools.r8.Disassemble.DisassembleCommand; import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.InternalCompilerError; import com.android.tools.r8.errors.Unimplemented; @@ -20,6 +21,7 @@ import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.OffOrAuto; +import com.android.tools.r8.utils.StringUtils; import com.beust.jcommander.internal.Lists; import com.google.common.io.ByteStreams; import java.io.File; @@ -292,6 +294,16 @@ mergedFromCompiledSeparately.writeToZip(out2, OutputMode.DexIndexed); ToolHelper.runArtNoVerificationErrors(out2.toString(), testPackage + "." + mainClass); + Path dissasemble1 = temp.newFolder().toPath().resolve("disassemble1.txt"); + Path dissasemble2 = temp.newFolder().toPath().resolve("disassemble2.txt"); + Disassemble.disassemble( + DisassembleCommand.builder().addProgramFiles(out1).setOutputPath(dissasemble1).build()); + Disassemble.disassemble( + DisassembleCommand.builder().addProgramFiles(out2).setOutputPath(dissasemble2).build()); + String content1 = StringUtils.join("\n", Files.readAllLines(dissasemble1)); + String content2 = StringUtils.join("\n", Files.readAllLines(dissasemble2)); + assertEquals(content1, content2); + Assert.assertArrayEquals( readResource(mergedFromCompiledSeparately.getDexProgramResourcesForTesting().get(0)), readResource(mergedFromCompiledTogether.getDexProgramResourcesForTesting().get(0)));
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java index 4e9353b..a8d57c0 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -105,9 +105,7 @@ .setMode(mode) .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR) .addKeepRuleFiles(MAIN_KEEP) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .apply(c -> FileUtils.writeTextFile(map, c.getProguardMap())) .writeToZip(jar); }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java index 874c095..e2eef5f 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -35,8 +35,6 @@ !parameters.canUseDefaultAndStaticInterfaceMethods(), i -> i.assertIsCompleteMergeGroup(B1.class, B2.class)) .assertNoOtherClassesMerged()) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java index de8d7b2..0af1e85 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -26,8 +26,6 @@ testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addKeepMainRule(Main.class) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java index 56be50b..7b5437e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -31,8 +31,6 @@ testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addKeepMainRule(Main.class) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleCrossGroupMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleCrossGroupMergingTest.java new file mode 100644 index 0000000..bbbec17 --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleCrossGroupMergingTest.java
@@ -0,0 +1,93 @@ +// 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.interfaces; + +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoUnusedInterfaceRemoval; +import com.android.tools.r8.NoVerticalClassMerging; +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; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ClassHierarchyCycleCrossGroupMergingTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public ClassHierarchyCycleCrossGroupMergingTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addHorizontallyMergedClassesInspector( + inspector -> + inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged()) + .enableNoHorizontalClassMergingAnnotations() + .enableNoUnusedInterfaceRemovalAnnotations() + .enableNoVerticalClassMergingAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main implements I, J, JSub, K, KSub, L { + + public static void main(String[] args) {} + } + + // As inputs to the interface merging policy that is supposed to avoid that merging leads to + // cycles in the class hierarchy, we will have the merge group {I, J, K, L}. Note that the + // classes JSub and KSub are not eligible for class merging (@NoHorizontalClassMerging). + // + // From this, we will form the merge group {I, J}. Note that the classes K and L are not + // eligible for being added to {I, J}, since this would lead to a cycle in the class hierarchy: + // I inherits from KSub, which inherits from K, and L inherits from JSub, which inherits from J. + // + // As a result of this, we create a new merge group {K}. Note that L is not eligible for being + // merged into {K}, since that would also lead to a cycle in the class hierarchy. In particular, + // if we form {K, L}, then {K, L} inherits from JSub, which inherits from {I, J}, which + // inherits from KSub, which inherits from {K, L}. + // + // Therefore, none of K and L should be eligible for merging in this case. + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface I extends KSub {} + + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface J {} + + @NoHorizontalClassMerging + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface JSub extends J {} + + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface K {} + + @NoHorizontalClassMerging + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface KSub extends K {} + + @NoUnusedInterfaceRemoval + @NoVerticalClassMerging + interface L extends JSub {} +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java index c1912b5..cc19dd2 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -66,7 +66,6 @@ if (enableInterfaceMergingInInitial) { options.horizontalClassMergerOptions().setEnableInterfaceMergingInInitial(); } - options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal(); }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java index 1c64d9d..e18ce93 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -50,8 +50,6 @@ inspector.assertIsCompleteMergeGroup(I.class, J.class); } }) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java index 3a33b0f..2324f03 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -51,8 +51,6 @@ inspector.assertNoClassesMerged(); } }) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java index c28bf26..05c63a7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; + import com.android.tools.r8.NoUnusedInterfaceRemoval; import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; @@ -34,8 +35,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java index 8cfb7af..91f0fbe 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
@@ -35,8 +35,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java index 13940fe..4588182 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
@@ -37,8 +37,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java index 3236862..c4af611 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
@@ -37,8 +37,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java index 2d1ac21..5a656d9 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -41,8 +41,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java index 7ca2089..08d1d2c 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
@@ -41,8 +41,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java index d059bbf..80160a4 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java
@@ -42,8 +42,6 @@ inspector .assertIsCompleteMergeGroup(I.class, J.class, K.class) .assertNoOtherClassesMerged()) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java index e2b196c..53086a7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
@@ -39,8 +39,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java index 100c54b..02239dd 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
@@ -35,8 +35,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java index 645a0c8..44bda30 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
@@ -35,8 +35,6 @@ .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations() .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java index 2284113..a84dbac 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -49,8 +49,6 @@ inspector.assertIsCompleteMergeGroup(I.class, J.class); } }) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java index 392c587..6905202 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
@@ -53,8 +53,6 @@ .assertNoOtherClassesMerged(); } }) - .addOptionsModification( - options -> options.horizontalClassMergerOptions().setEnableInterfaceMergingInFinal()) .enableInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/d8/NonNamedMemberClassTest.java b/src/test/java/com/android/tools/r8/d8/NonNamedMemberClassTest.java index a505c4f..9bb37c4 100644 --- a/src/test/java/com/android/tools/r8/d8/NonNamedMemberClassTest.java +++ b/src/test/java/com/android/tools/r8/d8/NonNamedMemberClassTest.java
@@ -5,11 +5,9 @@ import static org.hamcrest.CoreMatchers.containsString; -import com.android.tools.r8.D8TestCompileResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.ToolHelper.DexVm; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -25,7 +23,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withDexRuntimes().build(); + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); } public NonNamedMemberClassTest(TestParameters parameters) { @@ -34,17 +32,12 @@ @Test public void testD8() throws Exception { - D8TestCompileResult result = - testForD8() - .addProgramClassFileData(Dump.dump()) - .setMinApi(parameters.getRuntime()) - .compile(); - if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_6_0_1_HOST)) { - result.assertWarningMessageThatMatches(containsString("desugaring")); - } else { - result.assertOnlyInfos(); - } - result.assertInfoMessageThatMatches(containsString("missing EnclosingMethod")); + testForD8() + .addProgramClassFileData(Dump.dump()) + .setMinApi(parameters.getApiLevel()) + .compile() + .assertOnlyInfos() + .assertInfoMessageThatMatches(containsString("missing EnclosingMethod")); } // Compiled the following kt code:
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java index 00d6d45..2453bef 100644 --- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java +++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -3,14 +3,24 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.desugar.backports; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.ByteDataView; +import com.android.tools.r8.ClassFileConsumer; +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.OutputMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.ToolHelper.ArtCommandBuilder; +import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.desugar.backports.AbstractBackportTest.MiniAssert; import com.android.tools.r8.references.MethodReference; import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; @@ -24,6 +34,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -191,6 +202,62 @@ .inspect(this::checkExpectedSynthetics); } + @Test + public void testPerFileIntermediate() throws Exception { + ProcessResult result = runDoublePerFileCompilation(true); + assertEquals(result.toString(), 0, result.exitCode); + assertEquals(EXPECTED, result.stdout); + } + + @Test + public void testPerFileNonIntermediate() throws Exception { + try { + runDoublePerFileCompilation(false); + fail("Should expect the compilation to fail."); + } catch (CompilationFailedException e) { + assertThat( + e.getCause().getMessage(), + containsString("Attempt at compiling intermediate artifact without its context")); + } + } + + public ProcessResult runDoublePerFileCompilation(boolean intermediate) throws Exception { + List<byte[]> outputsRoundOne = new ArrayList<>(); + testForD8(Backend.CF) + .addProgramClasses(CLASSES) + .setMinApi(parameters.getApiLevel()) + .setIntermediate(true /* First round is always intermediate. */) + .setProgramConsumer( + new ClassFileConsumer.ForwardingConsumer(null) { + @Override + public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) { + outputsRoundOne.add(data.copyByteData()); + } + }) + .compile(); + + List<Path> outputsRoundTwo = new ArrayList<>(); + for (byte[] bytes : outputsRoundOne) { + outputsRoundTwo.add( + testForD8(parameters.getBackend()) + .addProgramClassFileData(bytes) + .setMinApi(parameters.getApiLevel()) + .setIntermediate(intermediate) + .compile() + .writeToZip()); + } + + if (parameters.isCfRuntime()) { + return ToolHelper.runJava( + parameters.getRuntime().asCf(), outputsRoundTwo, TestClass.class.getTypeName()); + } else { + ArtCommandBuilder builder = new ArtCommandBuilder(); + builder.setMainClass(TestClass.class.getTypeName()); + outputsRoundTwo.forEach(p -> builder.appendClasspath(p.toAbsolutePath().toString())); + return ToolHelper.runArtRaw(builder); + } + } + private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) { inspector.forAllClasses( clazz -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SuperAPIConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SuperAPIConversionTest.java new file mode 100644 index 0000000..d4e1c8f --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SuperAPIConversionTest.java
@@ -0,0 +1,100 @@ +// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar.desugaredlibrary.conversiontests; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import java.util.List; +import java.util.Random; +import java.util.stream.IntStream; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SuperAPIConversionTest extends DesugaredLibraryTestBase { + + private final TestParameters parameters; + private final boolean shrinkDesugaredLibrary; + + private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N; + + @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}") + public static List<Object[]> data() { + return buildParameters( + getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values()); + } + + public SuperAPIConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) { + this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; + this.parameters = parameters; + } + + @Test + public void testAPIConversionNoDesugaring() throws Exception { + Assume.assumeTrue("No need to test twice", shrinkDesugaredLibrary); + testForD8() + .addInnerClasses(SuperAPIConversionTest.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Executor.class) + .assertSuccessWithOutputLines("Head"); + } + + @Test + public void testAPIConversionDesugaringD8() throws Exception { + Assume.assumeFalse("TODO(b/189435770): fix", shrinkDesugaredLibrary); + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8() + .addInnerClasses(SuperAPIConversionTest.class) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .run(parameters.getRuntime(), Executor.class) + .assertSuccessWithOutputLines("$r8$wrapper$java$util$stream$IntStream$-V-WRP"); + } + + @Test + public void testAPIConversionDesugaringR8() throws Exception { + Assume.assumeFalse("TODO(b/189435770): fix", shrinkDesugaredLibrary); + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForR8(parameters.getBackend()) + .addInnerClasses(SuperAPIConversionTest.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(Executor.class) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .addDesugaredCoreLibraryRunClassPath( + this::buildDesugaredLibrary, + parameters.getApiLevel(), + keepRuleConsumer.get(), + shrinkDesugaredLibrary) + .run(parameters.getRuntime(), Executor.class) + .assertSuccessWithOutputLines("$r8$wrapper$java$util$stream$IntStream$-V-WRP"); + } + + static class ParallelRandom extends Random { + + @Override + public IntStream ints() { + return super.ints().parallel(); + } + } + + static class Executor { + + public static void main(String[] args) { + IntStream intStream = new ParallelRandom().ints(); + System.out.println(intStream.getClass().getSimpleName()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java index de1c949..1eac80e 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -77,9 +77,7 @@ options.desugarState = DesugarState.ON; options.cfToCfDesugar = true; })) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect(inspector -> assertNests(inspector, desugar)) .writeToZip(); }
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java index 31f7aba..cb549f5 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -50,9 +50,7 @@ .setMinApi(parameters.getApiLevel()) .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR) .addKeepRuleFiles(MAIN_KEEP) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect(this::assertNotEmpty) .inspect(Java11R8CompilationTest::assertNoNests); }
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java new file mode 100644 index 0000000..ee911d3 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java
@@ -0,0 +1,54 @@ +// 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.desugar.staticinterfacemethod; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class MutuallyRecursiveMethodsTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("42 is even? true"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevels().build(); + } + + public MutuallyRecursiveMethodsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForRuntime(parameters) + .addInnerClasses(MutuallyRecursiveMethodsTest.class) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + interface I { + static boolean isEven(int i) { + return i == 0 || isOdd(i - 1); + } + + static boolean isOdd(int i) { + return i != 0 && isEven(i - 1); + } + } + + static class TestClass { + + public static void main(String[] args) { + System.out.println("42 is even? " + I.isEven(42)); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/NoMissingClassWarningForNonInterfaceInvoke.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/NoMissingClassWarningForNonInterfaceInvoke.java new file mode 100644 index 0000000..08df42c --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/NoMissingClassWarningForNonInterfaceInvoke.java
@@ -0,0 +1,46 @@ +// 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.desugar.staticinterfacemethod; + +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 NoMissingClassWarningForNonInterfaceInvoke extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + public NoMissingClassWarningForNonInterfaceInvoke(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForD8(Backend.CF) + .addProgramClasses(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .setIntermediate(true) + .compile() + .assertNoWarningMessages(); + } + + static class MissingClass { + static void test() {} + } + + static class TestClass { + public static void main(String[] args) { + MissingClass.test(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java index 611cc70..f2fe2b8 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java
@@ -130,7 +130,8 @@ .assertAllInfoMessagesMatch( anyOf( containsString("Ignoring option: -optimizations"), - containsString("Proguard configuration rule does not match anything"))) + containsString("Proguard configuration rule does not match anything"), + containsString("Invalid signature"))) .apply(this::printProtoStats); }
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java index 4e784a3..ff19143 100644 --- a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java +++ b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
@@ -8,6 +8,7 @@ import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepAllProtosRule; import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepDynamicMethodSignatureRule; import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepNewMessageInfoSignatureRule; +import static com.android.tools.r8.utils.codeinspector.Matchers.invalidGenericArgumentApplicationCount; import static com.android.tools.r8.utils.codeinspector.Matchers.proguardConfigurationRuleDoesNotMatch; import static com.android.tools.r8.utils.codeinspector.Matchers.typeVariableNotInScope; import static org.hamcrest.CoreMatchers.anyOf; @@ -57,7 +58,10 @@ .inspectDiagnosticMessages( diagnostics -> diagnostics.assertAllInfosMatch( - anyOf(typeVariableNotInScope(), proguardConfigurationRuleDoesNotMatch()))) + anyOf( + typeVariableNotInScope(), + invalidGenericArgumentApplicationCount(), + proguardConfigurationRuleDoesNotMatch()))) .inspect(this::inspect); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java new file mode 100644 index 0000000..c38a3d7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
@@ -0,0 +1,140 @@ +// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.kotlin.metadata; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.ClassFileConsumer; +import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer; +import com.android.tools.r8.KotlinTestParameters; +import com.android.tools.r8.ProgramConsumer; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.shaking.ProguardKeepAttributes; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.nio.file.Path; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class MetadataRewriteInnerClassTest extends KotlinMetadataTestBase { + + private static final String EXPECTED = + StringUtils.lines( + "fun <init>(kotlin.Int):" + + " com.android.tools.r8.kotlin.metadata.nested_reflect.Outer.Nested", + "fun com.android.tools.r8.kotlin.metadata.nested_reflect.Outer.Inner.<init>(kotlin.Int):" + + " com.android.tools.r8.kotlin.metadata.nested_reflect.Outer.Inner"); + private static final String EXPECTED_OUTER_RENAMED = + StringUtils.lines( + "fun <init>(kotlin.Int): com.android.tools.r8.kotlin.metadata.nested_reflect.Nested", + "fun <init>(kotlin.Int): com.android.tools.r8.kotlin.metadata.nested_reflect.Inner"); + private static final String PKG_NESTED_REFLECT = PKG + ".nested_reflect"; + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}, {1}") + public static Collection<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(), + getKotlinTestParameters().withAllCompilersAndTargetVersions().build()); + } + + public MetadataRewriteInnerClassTest( + TestParameters parameters, KotlinTestParameters kotlinParameters) { + super(kotlinParameters); + this.parameters = parameters; + } + + private static final KotlinCompileMemoizer jarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/nested_reflect", "main")); + + @Test + public void smokeTest() throws Exception { + assumeTrue(parameters.isCfRuntime()); + Path libJar = jarMap.getForConfiguration(kotlinc, targetVersion); + testForRuntime(parameters) + .addProgramFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) + .run(parameters.getRuntime(), PKG_NESTED_REFLECT + ".MainKt") + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testMetadataOuterRenamed() throws Exception { + Path mainJar = + testForR8(parameters.getBackend()) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addClasspathFiles(ToolHelper.getKotlinReflectJar(kotlinc)) + .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc)) + .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion)) + .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Nested { *; }") + .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Inner { *; }") + .addKeepMainRule(PKG_NESTED_REFLECT + ".MainKt") + .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> inspectPruned(inspector, true)) + .writeToZip(); + + runD8(mainJar, EXPECTED_OUTER_RENAMED); + } + + @Test + public void testMetadataOuterNotRenamed() throws Exception { + Path mainJar = + testForR8(parameters.getBackend()) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addClasspathFiles(ToolHelper.getKotlinReflectJar(kotlinc)) + .addClasspathFiles(ToolHelper.getKotlinAnnotationJar(kotlinc)) + .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion)) + .addKeepAttributeInnerClassesAndEnclosingMethod() + .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer { *; }") + .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Nested { *; }") + .addKeepRules("-keep public class " + PKG_NESTED_REFLECT + ".Outer$Inner { *; }") + .addKeepMainRule(PKG_NESTED_REFLECT + ".MainKt") + .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> inspectPruned(inspector, false)) + .writeToZip(); + + runD8(mainJar, EXPECTED); + } + + private void runD8(Path jar, String expected) throws Exception { + Path output = temp.newFile("output.zip").toPath(); + ProgramConsumer programConsumer = + parameters.isCfRuntime() + ? new ClassFileConsumer.ArchiveConsumer(output, true) + : new ArchiveConsumer(output, true); + testForD8(parameters.getBackend()) + .addProgramFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), jar) + .setMinApi(parameters.getApiLevel()) + .setProgramConsumer(programConsumer) + .addOptionsModification( + options -> { + // Needed for passing kotlin_builtin files to output. + options.testing.enableD8ResourcesPassThrough = true; + options.dataResourceConsumer = options.programConsumer.getDataResourceConsumer(); + }) + .run(parameters.getRuntime(), PKG_NESTED_REFLECT + ".MainKt") + .assertSuccessWithOutput(expected); + } + + private void inspectPruned(CodeInspector inspector, boolean outerRenamed) { + assertThat( + inspector.clazz(PKG_NESTED_REFLECT + ".Outer"), + outerRenamed ? isPresentAndRenamed() : isPresent()); + assertThat(inspector.clazz(PKG_NESTED_REFLECT + ".Outer$Nested"), isPresent()); + assertThat(inspector.clazz(PKG_NESTED_REFLECT + ".Outer$Inner"), isPresent()); + } +}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/nested_reflect/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/nested_reflect/main.kt new file mode 100644 index 0000000..f73e134 --- /dev/null +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/nested_reflect/main.kt
@@ -0,0 +1,19 @@ +// 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.kotlin.metadata.nested_reflect + +import kotlin.reflect.full.primaryConstructor + +class Outer { + data class Nested(val data: Int) + inner class Inner(val data: Int) +} + +fun main() { + val nestedPrimaryCtr = Outer.Nested::class.primaryConstructor + println(nestedPrimaryCtr?.toString() ?: "Cannot find primary constructor") + val innerPrimaryCtr = Outer.Inner::class.primaryConstructor + println(innerPrimaryCtr?.toString() ?: "Cannot find primary constructor") +}
diff --git a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java index a8e337e..04ec6b4 100644 --- a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java +++ b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils; import java.io.IOException; @@ -51,14 +52,16 @@ } @Test - public void testKeepingMethodParametersR8() - throws ExecutionException, CompilationFailedException, IOException { + public void testKeepingMethodParametersR8() throws Exception { R8TestRunResult runResult = testForR8(parameters.getBackend()) .addProgramClassFileData(MethodParametersTestDump.dump()) .addKeepClassAndMembersRulesWithAllowObfuscation(MethodParametersTest.class) .addKeepMainRule(MethodParametersTest.class) - .addKeepRules(keepMethodParameters ? "-keepattributes MethodParameters" : "") + .addKeepAttributeSourceFile() + .applyIf( + keepMethodParameters, + builder -> builder.addKeepAttributes(ProguardKeepAttributes.METHOD_PARAMETERS)) .setMinApi(keepMethodParameters ? AndroidApiLevel.O : AndroidApiLevel.L) // java.lang.reflect.Parameter was introduced in API level 26 (O). .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java index 9947888..33dfbaf 100644 --- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java +++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -101,9 +101,7 @@ .addDontWarnGoogle() .addDontWarnJavax() .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement") - .allowDiagnosticInfoMessages() - .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation); + .compile(); } @Test @@ -128,9 +126,7 @@ .addDontWarnGoogle() .addDontWarnJavax() .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement") - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect( inspector -> { ClassSubject clazz = inspector.clazz(CLASS_WITH_ANNOTATED_METHOD); @@ -146,9 +142,7 @@ // TODO(b/159971974): Technically this rule does not hit anything and should fail due to // missing allowUnusedProguardConfigurationRules() .addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }") - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect(inspector -> assertEquals(0, inspector.allClasses().size())); } @@ -158,9 +152,7 @@ .addProgramFiles(R8_JAR) .addKeepClassRules(CLASS_WITH_ANNOTATED_METHOD) .addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }") - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect( inspector -> { assertEquals(1, inspector.allClasses().size()); @@ -197,9 +189,7 @@ .addProgramFiles(R8_JAR) .addKeepClassRules(CLASS_WITH_ANNOTATED_METHOD) .addKeepRules("-if class * -keep class <1> { @" + PRESENT_ANNOTATION + " *** *(...); }") - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .inspect( inspector -> { assertEquals(1, inspector.allClasses().size()); @@ -231,10 +221,8 @@ .addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }") .addDontWarnGoogle() .addDontWarnJavaxNullableAnnotation() - .allowDiagnosticInfoMessages() .apply(this::configureHorizontalClassMerging) .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .graphInspector(); GraphInspector ifThenKeepClassMembersInspector = @@ -253,9 +241,7 @@ .addDontWarnGoogle() .addDontWarnJavaxNullableAnnotation() .apply(this::configureHorizontalClassMerging) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .graphInspector(); assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector); @@ -275,9 +261,7 @@ .addDontWarnGoogle() .addDontWarnJavaxNullableAnnotation() .apply(this::configureHorizontalClassMerging) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .graphInspector(); assertRetainedClassesEqual(referenceInspector, ifThenKeepClassesWithMembersInspector); @@ -299,9 +283,7 @@ .addDontWarnGoogle() .addDontWarnJavaxNullableAnnotation() .apply(this::configureHorizontalClassMerging) - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .graphInspector(); assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector); }
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java index ac22ab7..94bdbf1 100644 --- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java +++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -48,9 +48,7 @@ .addKeepRuleFiles(MAIN_KEEP) .addKeepRules(WHY_ARE_YOU_KEEPING_ALL) .collectStdout() - .allowDiagnosticInfoMessages() .compile() - .apply(TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .assertStdoutThatMatches(containsString("referenced in keep rule")) // TODO(b/124655065): We should always know the reason for keeping. // It is OK if this starts failing while the kept-graph API is incomplete, in which case
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java index 4f4b656..cbbbb14 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -227,6 +227,12 @@ return DiagnosticsMatcher.diagnosticMessage(containsString("A type variable is not in scope")); } + public static Matcher<Diagnostic> invalidGenericArgumentApplicationCount() { + return DiagnosticsMatcher.diagnosticMessage( + containsString( + "The applied generic arguments have different count than the expected formals")); + } + public static Matcher<Diagnostic> proguardConfigurationRuleDoesNotMatch() { return DiagnosticsMatcher.diagnosticMessage( containsString("Proguard configuration rule does not match anything"));