blob: 90c112cb9f42e924c7de911c95eec46f7a45c82f [file] [log] [blame]
// 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.AppInfoWithSubtyping;
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;
public class ArrayTypeLatticeElement extends ReferenceTypeLatticeElement {
private final TypeLatticeElement memberTypeLattice;
// On-demand link between other nullability-variants.
private final NullabilityVariants<ArrayTypeLatticeElement> variants;
public static ArrayTypeLatticeElement create(
TypeLatticeElement memberTypeLattice, Nullability nullability) {
return NullabilityVariants.create(
nullability,
(variants) -> new ArrayTypeLatticeElement(memberTypeLattice, nullability, variants));
}
private ArrayTypeLatticeElement(
TypeLatticeElement memberTypeLattice,
Nullability nullability,
NullabilityVariants<ArrayTypeLatticeElement> variants) {
super(nullability);
assert memberTypeLattice.isPrimitive() || memberTypeLattice.nullability().isMaybeNull();
this.memberTypeLattice = memberTypeLattice;
this.variants = variants;
}
public DexType getArrayType(DexItemFactory factory) {
TypeLatticeElement baseTypeLattice = getArrayBaseTypeLattice();
DexType baseType;
if (baseTypeLattice.isPrimitive()) {
baseType = baseTypeLattice.asPrimitiveTypeLatticeElement().toDexType(factory);
} else {
assert baseTypeLattice.isClassType();
baseType = baseTypeLattice.asClassTypeLatticeElement().getClassType();
}
return factory.createArrayType(getNesting(), baseType);
}
int getNesting() {
int nesting = 1;
TypeLatticeElement member = getArrayMemberTypeAsMemberType();
while (member.isArrayType()) {
++nesting;
member = member.asArrayTypeLatticeElement().getArrayMemberTypeAsMemberType();
}
return nesting;
}
TypeLatticeElement getArrayMemberTypeAsMemberType() {
return memberTypeLattice;
}
public TypeLatticeElement getArrayMemberTypeAsValueType() {
return memberTypeLattice.isFineGrainedType() ? INT : memberTypeLattice;
}
public TypeLatticeElement getArrayBaseTypeLattice() {
TypeLatticeElement base = getArrayMemberTypeAsMemberType();
while (base.isArrayType()) {
base = base.asArrayTypeLatticeElement().getArrayMemberTypeAsMemberType();
}
return base;
}
private ArrayTypeLatticeElement createVariant(
Nullability nullability, NullabilityVariants<ArrayTypeLatticeElement> variants) {
assert this.nullability != nullability;
return new ArrayTypeLatticeElement(memberTypeLattice, nullability, variants);
}
@Override
public ReferenceTypeLatticeElement getOrCreateVariant(Nullability nullability) {
ArrayTypeLatticeElement variant = variants.get(nullability);
if (variant != null) {
return variant;
}
return variants.getOrCreateElement(nullability, this::createVariant);
}
@Override
public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithSubtyping> appView) {
return memberTypeLattice.isBasedOnMissingClass(appView);
}
@Override
public boolean isArrayType() {
return true;
}
@Override
public ArrayTypeLatticeElement asArrayTypeLatticeElement() {
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 ArrayTypeLatticeElement)) {
return false;
}
ArrayTypeLatticeElement other = (ArrayTypeLatticeElement) o;
if (nullability() != other.nullability()) {
return false;
}
return memberTypeLattice.equals(other.memberTypeLattice);
}
@Override
public int hashCode() {
return Objects.hash(nullability, memberTypeLattice);
}
ReferenceTypeLatticeElement join(ArrayTypeLatticeElement other, AppView<?> appView) {
Nullability nullability = nullability().join(other.nullability());
ReferenceTypeLatticeElement 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 ReferenceTypeLatticeElement joinMember(
TypeLatticeElement aMember,
TypeLatticeElement 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()) {
TypeLatticeElement join =
joinMember(
aMember.asArrayTypeLatticeElement().memberTypeLattice,
bMember.asArrayTypeLatticeElement().memberTypeLattice,
appView,
maybeNull());
return join == null ? null : ArrayTypeLatticeElement.create(join, nullability);
}
if (aMember.isClassType() && bMember.isClassType()) {
ReferenceTypeLatticeElement join =
aMember.asClassTypeLatticeElement().join(bMember.asClassTypeLatticeElement(), appView);
return ArrayTypeLatticeElement.create(join, nullability);
}
if (aMember.isPrimitive() || bMember.isPrimitive()) {
return aMember.objectClassType(appView, nullability);
}
return aMember.objectArrayType(appView, nullability);
}
}