blob: 5f5036718d549b13385c2e7d87dbc8ad9e3e6637 [file] [log] [blame]
// Copyright (c) 2020, 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.utils.codeinspector;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import kotlinx.metadata.KmAnnotation;
import kotlinx.metadata.KmFlexibleTypeUpperBound;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeVisitor;
import kotlinx.metadata.jvm.JvmExtensionsKt;
public class KmTypeSubject extends Subject {
private final CodeInspector codeInspector;
private final KmType kmType;
KmTypeSubject(CodeInspector codeInspector, KmType kmType) {
assert kmType != null;
this.codeInspector = codeInspector;
this.kmType = kmType;
}
// TODO(b/151195430): This is a dup of DescriptorUtils#getDescriptorFromKmType
static String getDescriptorFromKmType(KmType kmType) {
if (kmType == null) {
return null;
}
Box<String> descriptor = new Box<>(null);
kmType.accept(new KmTypeVisitor() {
@Override
public void visitClass(String name) {
// We don't check Kotlin types in tests, but be aware of the relocation issue.
// See b/70169921#comment25 for more details.
assert descriptor.get() == null;
descriptor.set(getDescriptorFromKotlinClassifier(name));
}
@Override
public void visitTypeAlias(String name) {
assert descriptor.get() == null;
descriptor.set(getDescriptorFromKotlinClassifier(name));
}
});
return descriptor.get();
}
public String descriptor() {
return getDescriptorFromKmType(kmType);
}
public List<KmTypeProjectionSubject> typeArguments() {
return kmType.getArguments().stream()
.map(kmTypeProjection -> new KmTypeProjectionSubject(codeInspector, kmTypeProjection))
.collect(Collectors.toList());
}
public KmClassifierSubject classifier() {
return new KmClassifierSubject(kmType.classifier);
}
public KmFlexibleTypeUpperBound getFlexibleUpperBound() {
return kmType.getFlexibleTypeUpperBound();
}
@Override
public boolean isPresent() {
return true;
}
@Override
public boolean isRenamed() {
ClassSubject classSubject = codeInspector.clazz(Reference.classFromDescriptor(descriptor()));
return classSubject.isRenamed();
}
@Override
public boolean isSynthetic() {
throw new Unreachable("Cannot determine if a type is synthetic");
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof KmTypeSubject)) {
return false;
}
return areEqual(this.kmType, ((KmTypeSubject) obj).kmType, true);
}
public boolean equalUpToAbbreviatedType(KmTypeSubject other) {
if (other == null) {
return false;
}
return areEqual(this.kmType, other.kmType, false);
}
public static boolean areEqual(KmType one, KmType other, boolean checkInnerTypeReferences) {
if (one == null && other == null) {
return true;
}
if (one == null || other == null) {
return false;
}
if (one.getFlags() != other.getFlags()) {
return false;
}
if (!one.classifier.toString().equals(other.classifier.toString())) {
return false;
}
if (one.getArguments().size() != other.getArguments().size()) {
return false;
}
for (int i = 0; i < one.getArguments().size(); i++) {
if (!KmTypeProjectionSubject.areEqual(
one.getArguments().get(i), other.getArguments().get(i))) {
return false;
}
}
if (checkInnerTypeReferences
&& !areEqual(
one.getAbbreviatedType(), other.getAbbreviatedType(), checkInnerTypeReferences)) {
return false;
}
if (!areEqual(one.getOuterType(), other.getOuterType(), checkInnerTypeReferences)) {
return false;
}
if ((one.getFlexibleTypeUpperBound() == null) != (other.getFlexibleTypeUpperBound() == null)
&& checkInnerTypeReferences) {
return false;
}
if (one.getFlexibleTypeUpperBound() != null && checkInnerTypeReferences) {
if (!Objects.equals(
one.getFlexibleTypeUpperBound().getTypeFlexibilityId(),
other.getFlexibleTypeUpperBound().getTypeFlexibilityId())) {
return false;
}
if (!areEqual(
one.getFlexibleTypeUpperBound().getType(),
other.getFlexibleTypeUpperBound().getType(),
checkInnerTypeReferences)) {
return false;
}
}
if (JvmExtensionsKt.isRaw(one) != JvmExtensionsKt.isRaw(other)) {
return false;
}
List<KmAnnotation> annotationsOne = JvmExtensionsKt.getAnnotations(one);
List<KmAnnotation> annotationsOther = JvmExtensionsKt.getAnnotations(other);
if (annotationsOne.size() != annotationsOther.size()) {
return false;
}
for (int i = 0; i < annotationsOne.size(); i++) {
KmAnnotation kmAnnotationOne = annotationsOne.get(i);
KmAnnotation kmAnnotationOther = annotationsOther.get(i);
if (!kmAnnotationOne.getClassName().equals(kmAnnotationOther.getClassName())) {
return false;
}
if (!kmAnnotationOne
.getArguments()
.keySet()
.equals(kmAnnotationOther.getArguments().keySet())) {
return false;
}
assert false : "Not defined how to compare kmAnnotationArguments";
}
return true;
}
}