blob: a744297bf02154b8f1ad2581f536fa90b134d56b [file] [log] [blame]
// 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.ClassReferenceUtils.getClassReferenceComparator;
import static com.android.tools.r8.utils.FieldReferenceUtils.getFieldReferenceComparator;
import static com.android.tools.r8.utils.MethodReferenceUtils.getMethodReferenceComparator;
import com.android.tools.r8.diagnostic.DefinitionContext;
import com.android.tools.r8.diagnostic.MissingClassInfo;
import com.android.tools.r8.diagnostic.MissingDefinitionInfo;
import com.android.tools.r8.diagnostic.MissingFieldInfo;
import com.android.tools.r8.diagnostic.MissingMethodInfo;
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.MethodReferenceUtils;
import java.util.Comparator;
import java.util.function.Consumer;
public class MissingDefinitionInfoUtils {
private static final Comparator<MissingDefinitionInfo> COMPARATOR =
(info, other) -> {
if (info.isMissingClass()) {
ClassReference classReference = info.asMissingClass().getClassReference();
if (other.isMissingClass()) {
return ClassReferenceUtils.compare(
classReference, other.asMissingClass().getClassReference());
}
if (other.isMissingField()) {
return ClassReferenceUtils.compare(
classReference, other.asMissingField().getFieldReference());
}
return ClassReferenceUtils.compare(
classReference, other.asMissingMethod().getMethodReference());
}
if (info.isMissingField()) {
FieldReference fieldReference = info.asMissingField().getFieldReference();
if (other.isMissingClass()) {
return FieldReferenceUtils.compare(
fieldReference, other.asMissingClass().getClassReference());
}
if (other.isMissingField()) {
return FieldReferenceUtils.compare(
fieldReference, other.asMissingField().getFieldReference());
}
return FieldReferenceUtils.compare(
fieldReference, other.asMissingMethod().getMethodReference());
}
MethodReference methodReference = info.asMissingMethod().getMethodReference();
if (other.isMissingClass()) {
return MethodReferenceUtils.compare(
methodReference, other.asMissingClass().getClassReference());
}
if (other.isMissingField()) {
return MethodReferenceUtils.compare(
methodReference, other.asMissingField().getFieldReference());
}
return MethodReferenceUtils.compare(
methodReference, other.asMissingMethod().getMethodReference());
};
public static void accept(
MissingDefinitionInfo missingDefinitionInfo,
Consumer<MissingClassInfo> missingClassInfoConsumer,
Consumer<MissingFieldInfo> missingFieldInfoConsumer,
Consumer<MissingMethodInfo> missingMethodInfoConsumer) {
if (missingDefinitionInfo.isMissingClass()) {
missingClassInfoConsumer.accept(missingDefinitionInfo.asMissingClass());
} else if (missingDefinitionInfo.isMissingField()) {
missingFieldInfoConsumer.accept(missingDefinitionInfo.asMissingField());
} else {
assert missingDefinitionInfo.isMissingMethod();
missingMethodInfoConsumer.accept(missingDefinitionInfo.asMissingMethod());
}
}
public static Comparator<MissingDefinitionInfo> getComparator() {
return COMPARATOR;
}
public static void writeDiagnosticMessage(
StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
MissingDefinitionInfoUtils.accept(
missingDefinitionInfo,
missingClassInfo ->
builder
.append("Missing class ")
.append(missingClassInfo.getClassReference().getTypeName()),
missingFieldInfo ->
builder
.append("Missing field ")
.append(FieldReferenceUtils.toSourceString(missingFieldInfo.getFieldReference())),
missingMethodInfo ->
builder
.append("Missing method ")
.append(
MethodReferenceUtils.toSourceString(missingMethodInfo.getMethodReference())));
writeReferencedFromSuffix(builder, missingDefinitionInfo);
}
private static void writeReferencedFromSuffix(
StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo) {
Box<ClassReference> classContext = new Box<>();
Box<FieldReference> fieldContext = new Box<>();
Box<MethodReference> methodContext = new Box<>();
for (DefinitionContext missingDefinitionContext :
missingDefinitionInfo.getReferencedFromContexts()) {
DefinitionContextUtils.accept(
missingDefinitionContext,
missingDefinitionClassContext ->
classContext.setMin(
missingDefinitionClassContext.getClassReference(), getClassReferenceComparator()),
missingDefinitionFieldContext ->
fieldContext.setMin(
missingDefinitionFieldContext.getFieldReference(), getFieldReferenceComparator()),
missingDefinitionMethodContext ->
methodContext.setMin(
missingDefinitionMethodContext.getMethodReference(),
getMethodReferenceComparator()));
}
// TODO(b/186506586): Reenable assert once trace references also provide contextual information.
// assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
if (fieldContext.isSet()) {
writeReferencedFromSuffix(
builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
} else if (methodContext.isSet()) {
writeReferencedFromSuffix(
builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
} else if (classContext.isSet()) {
writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
}
}
private static void writeReferencedFromSuffix(
StringBuilder builder, MissingDefinitionInfo missingDefinitionInfo, String referencedFrom) {
int numberOfOtherContexts = missingDefinitionInfo.getReferencedFromContexts().size() - 1;
assert numberOfOtherContexts >= 0;
builder.append(" (referenced from: ").append(referencedFrom);
if (numberOfOtherContexts >= 1) {
builder.append(" and ").append(numberOfOtherContexts).append(" other context");
if (numberOfOtherContexts >= 2) {
builder.append("s");
}
}
builder.append(")");
}
}