Ensure missing definitions diagnostic message is deterministic
Change-Id: If5c7c376ce718f2fc0ed71d592c326892e757d68
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
new file mode 100644
index 0000000..2b0e375
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
@@ -0,0 +1,104 @@
+// 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.diagnostic.internal;
+
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
+import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.util.Comparator;
+
+public class MissingDefinitionInfoUtils {
+
+ private static final Comparator<MissingDefinitionInfo> COMPARATOR =
+ (info, other) -> {
+ IntBox result = new IntBox();
+ if (isMissingClassInfo(info)) {
+ ClassReference classReference = getClassReference(info);
+ other.getMissingDefinition(
+ otherClassReference ->
+ result.set(ClassReferenceUtils.compare(classReference, otherClassReference)),
+ otherFieldReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ classReference, otherFieldReference.getHolderClass())),
+ otherMethodReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ classReference, otherMethodReference.getHolderClass())));
+ } else if (isMissingFieldInfo(info)) {
+ FieldReference fieldReference = getFieldReference(info);
+ other.getMissingDefinition(
+ otherClassReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ fieldReference.getHolderClass(), otherClassReference)),
+ otherFieldReference ->
+ result.set(FieldReferenceUtils.compare(fieldReference, otherFieldReference)),
+ otherMethodReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ fieldReference.getHolderClass(), otherMethodReference.getHolderClass())));
+ } else {
+ MethodReference methodReference = getMethodReference(info);
+ other.getMissingDefinition(
+ otherClassReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ methodReference.getHolderClass(), otherClassReference)),
+ otherFieldReference ->
+ result.set(
+ ClassReferenceUtils.compare(
+ methodReference.getHolderClass(), otherFieldReference.getHolderClass())),
+ otherMethodReference ->
+ result.set(MethodReferenceUtils.compare(methodReference, otherMethodReference)));
+ }
+ return result.get();
+ };
+
+ public static Comparator<MissingDefinitionInfo> getComparator() {
+ return COMPARATOR;
+ }
+
+ public static ClassReference getClassReference(MissingDefinitionInfo missingDefinitionInfo) {
+ Box<ClassReference> classReference = new Box<>();
+ missingDefinitionInfo.getMissingDefinition(
+ classReference::set, emptyConsumer(), emptyConsumer());
+ return classReference.get();
+ }
+
+ public static boolean isMissingClassInfo(MissingDefinitionInfo missingDefinitionInfo) {
+ return getClassReference(missingDefinitionInfo) != null;
+ }
+
+ public static FieldReference getFieldReference(MissingDefinitionInfo missingDefinitionInfo) {
+ Box<FieldReference> fieldReference = new Box<>();
+ missingDefinitionInfo.getMissingDefinition(
+ emptyConsumer(), fieldReference::set, emptyConsumer());
+ return fieldReference.get();
+ }
+
+ public static boolean isMissingFieldInfo(MissingDefinitionInfo missingDefinitionInfo) {
+ return getFieldReference(missingDefinitionInfo) != null;
+ }
+
+ public static MethodReference getMethodReference(MissingDefinitionInfo missingDefinitionInfo) {
+ Box<MethodReference> methodReference = new Box<>();
+ missingDefinitionInfo.getMissingDefinition(
+ emptyConsumer(), emptyConsumer(), methodReference::set);
+ return methodReference.get();
+ }
+
+ public static boolean isMissingMethodInfo(MissingDefinitionInfo missingDefinitionInfo) {
+ return getMethodReference(missingDefinitionInfo) != null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
index f9455a0..9c1f322 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
@@ -20,8 +20,10 @@
import com.android.tools.r8.utils.FieldReferenceUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
public class MissingDefinitionsDiagnosticImpl implements MissingDefinitionsDiagnostic {
@@ -41,6 +43,13 @@
return missingDefinitions;
}
+ private Collection<MissingDefinitionInfo> getMissingDefinitionsWithDeterministicOrder() {
+ List<MissingDefinitionInfo> missingDefinitionsWithDeterministicOrder =
+ new ArrayList<>(getMissingDefinitions());
+ missingDefinitionsWithDeterministicOrder.sort(MissingDefinitionInfoUtils.getComparator());
+ return missingDefinitionsWithDeterministicOrder;
+ }
+
/** A missing class(es) failure can generally not be attributed to a single origin. */
@Override
public Origin getOrigin() {
@@ -56,7 +65,8 @@
@Override
public String getDiagnosticMessage() {
StringBuilder builder = new StringBuilder();
- Iterator<MissingDefinitionInfo> missingDefinitionsIterator = missingDefinitions.iterator();
+ Iterator<MissingDefinitionInfo> missingDefinitionsIterator =
+ getMissingDefinitionsWithDeterministicOrder().iterator();
// The diagnostic is always non-empty.
assert missingDefinitionsIterator.hasNext();
diff --git a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
index eb5659d..171344e 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
@@ -12,6 +12,10 @@
private static final Comparator<ClassReference> COMPARATOR =
Comparator.comparing(ClassReference::getDescriptor);
+ public static int compare(ClassReference classReference, ClassReference other) {
+ return getClassReferenceComparator().compare(classReference, other);
+ }
+
public static Comparator<ClassReference> getClassReferenceComparator() {
return COMPARATOR;
}
diff --git a/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
index 062960e..ebf7821 100644
--- a/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
@@ -29,6 +29,10 @@
return getTypeReferenceComparator().compare(field.getFieldType(), other.getFieldType());
};
+ public static int compare(FieldReference fieldReference, FieldReference other) {
+ return getFieldReferenceComparator().compare(fieldReference, other);
+ }
+
public static FieldReference fieldFromField(Class<?> clazz, String name) {
try {
return Reference.fieldFromField(clazz.getDeclaredField(name));
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 3719535..4d36fb9 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -52,6 +52,10 @@
return method.getFormalTypes().size() - other.getFormalTypes().size();
};
+ public static int compare(MethodReference methodReference, MethodReference other) {
+ return getMethodReferenceComparator().compare(methodReference, other);
+ }
+
public static Comparator<MethodReference> getMethodReferenceComparator() {
return COMPARATOR;
}