| // Copyright (c) 2017, 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.ir.analysis.type; |
| |
| import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; |
| |
| import com.android.tools.r8.graph.AppInfoWithClassHierarchy; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexType; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.function.Function; |
| |
| public class ArrayTypeElement extends ReferenceTypeElement { |
| |
| private final TypeElement memberTypeLattice; |
| |
| // On-demand link between other nullability-variants. |
| private final NullabilityVariants<ArrayTypeElement> variants; |
| |
| public static ArrayTypeElement create(TypeElement memberTypeLattice, Nullability nullability) { |
| return NullabilityVariants.create( |
| nullability, (variants) -> new ArrayTypeElement(memberTypeLattice, nullability, variants)); |
| } |
| |
| private ArrayTypeElement( |
| TypeElement memberTypeLattice, |
| Nullability nullability, |
| NullabilityVariants<ArrayTypeElement> variants) { |
| super(nullability); |
| assert memberTypeLattice.isPrimitiveType() || memberTypeLattice.nullability().isMaybeNull(); |
| this.memberTypeLattice = memberTypeLattice; |
| this.variants = variants; |
| } |
| |
| public DexType toDexType(DexItemFactory factory) { |
| TypeElement baseTypeLattice = getBaseType(); |
| DexType baseType; |
| if (baseTypeLattice.isPrimitiveType()) { |
| baseType = baseTypeLattice.asPrimitiveType().toDexType(factory); |
| } else { |
| assert baseTypeLattice.isClassType(); |
| baseType = baseTypeLattice.asClassType().getClassType(); |
| } |
| return factory.createArrayType(getNesting(), baseType); |
| } |
| |
| public int getNesting() { |
| int nesting = 1; |
| TypeElement member = getMemberType(); |
| while (member.isArrayType()) { |
| ++nesting; |
| member = member.asArrayType().getMemberType(); |
| } |
| return nesting; |
| } |
| |
| @Override |
| public boolean isPrimitiveArrayType() { |
| return memberTypeLattice.isPrimitiveType(); |
| } |
| |
| public TypeElement getMemberType() { |
| return memberTypeLattice; |
| } |
| |
| public TypeElement getMemberTypeAsValueType() { |
| return memberTypeLattice.isFineGrainedType() ? getInt() : memberTypeLattice; |
| } |
| |
| public TypeElement getBaseType() { |
| TypeElement base = getMemberType(); |
| while (base.isArrayType()) { |
| base = base.asArrayType().getMemberType(); |
| } |
| return base; |
| } |
| |
| private ArrayTypeElement createVariant( |
| Nullability nullability, NullabilityVariants<ArrayTypeElement> variants) { |
| assert this.nullability != nullability; |
| return new ArrayTypeElement(memberTypeLattice, nullability, variants); |
| } |
| |
| @Override |
| public ReferenceTypeElement getOrCreateVariant(Nullability nullability) { |
| ArrayTypeElement variant = variants.get(nullability); |
| if (variant != null) { |
| return variant; |
| } |
| return variants.getOrCreateElement(nullability, this::createVariant); |
| } |
| |
| @Override |
| public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithClassHierarchy> appView) { |
| return memberTypeLattice.isBasedOnMissingClass(appView); |
| } |
| |
| @Override |
| public boolean isArrayType() { |
| return true; |
| } |
| |
| @Override |
| public ArrayTypeElement asArrayType() { |
| return this; |
| } |
| |
| @Override |
| public String toString() { |
| return nullability.toString() + " (" + memberTypeLattice.toString() + "[])"; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof ArrayTypeElement)) { |
| return false; |
| } |
| ArrayTypeElement other = (ArrayTypeElement) o; |
| if (nullability() != other.nullability()) { |
| return false; |
| } |
| return memberTypeLattice.equals(other.memberTypeLattice); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(nullability, memberTypeLattice); |
| } |
| |
| @Override |
| public ArrayTypeElement fixupClassTypeReferences( |
| AppView<? extends AppInfoWithClassHierarchy> appView, |
| Function<DexType, DexType> mapping, |
| Set<DexType> prunedTypes) { |
| if (memberTypeLattice.isReferenceType()) { |
| TypeElement substitutedMemberType = |
| memberTypeLattice.fixupClassTypeReferences(appView, mapping, prunedTypes); |
| if (substitutedMemberType != memberTypeLattice) { |
| return ArrayTypeElement.create(substitutedMemberType, nullability); |
| } |
| } |
| return this; |
| } |
| |
| ReferenceTypeElement join(ArrayTypeElement other, AppView<?> appView) { |
| Nullability nullability = nullability().join(other.nullability()); |
| ReferenceTypeElement join = |
| joinMember(this.memberTypeLattice, other.memberTypeLattice, appView, nullability); |
| if (join == null) { |
| // Check if other has the right nullability before creating it. |
| if (other.nullability == nullability) { |
| return other; |
| } else { |
| return getOrCreateVariant(nullability); |
| } |
| } else { |
| assert join.nullability == nullability; |
| return join; |
| } |
| } |
| |
| private static ReferenceTypeElement joinMember( |
| TypeElement aMember, TypeElement bMember, AppView<?> appView, Nullability nullability) { |
| if (aMember.equals(bMember)) { |
| // Return null indicating the join is the same as the member to avoid object allocation. |
| return null; |
| } |
| if (aMember.isArrayType() && bMember.isArrayType()) { |
| TypeElement join = |
| joinMember( |
| aMember.asArrayType().memberTypeLattice, |
| bMember.asArrayType().memberTypeLattice, |
| appView, |
| maybeNull()); |
| return join == null ? null : ArrayTypeElement.create(join, nullability); |
| } |
| if (aMember.isClassType() && bMember.isClassType()) { |
| ReferenceTypeElement join = aMember.asClassType().join(bMember.asClassType(), appView); |
| return ArrayTypeElement.create(join, nullability); |
| } |
| if (aMember.isPrimitiveType() || bMember.isPrimitiveType()) { |
| return objectClassType(appView, nullability); |
| } |
| return objectArrayType(appView, nullability); |
| } |
| } |