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