blob: 6b7717100381346a17504a0f73b18a4bea1be323 [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 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;
}
@Override
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 ArrayTypeElement getOrCreateVariant(Nullability nullability) {
return nullability.equals(nullability())
? this
: 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(getMemberType(), other.getMemberType(), 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;
}
}
ReferenceTypeElement join(ClassTypeElement other, AppView<?> appView) {
return other.join(this, appView);
}
@Override
public ReferenceTypeElement join(ReferenceTypeElement other, AppView<?> appView) {
if (other.isArrayType()) {
return join(other.asArrayType(), appView);
}
if (other.isClassType()) {
return join(other.asClassType(), appView);
}
assert other.isNullType();
return joinNullability(other.nullability());
}
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 aMemberMember = aMember.asArrayType().getMemberType();
TypeElement bMemberMember = bMember.asArrayType().getMemberType();
TypeElement join =
joinMember(
aMemberMember,
bMemberMember,
appView,
aMember.nullability().join(bMember.nullability()));
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()) {
if (appView.enableWholeProgramOptimizations()) {
assert appView.hasClassHierarchy();
DexItemFactory dexItemFactory = appView.dexItemFactory();
InterfaceCollection interfaceCollection =
InterfaceCollection.builder()
.addKnownInterface(dexItemFactory.cloneableType)
.addKnownInterface(dexItemFactory.serializableType)
.build();
return ClassTypeElement.create(
dexItemFactory.objectType,
nullability,
appView.withClassHierarchy(),
interfaceCollection);
}
return objectClassType(appView, nullability);
}
return objectArrayType(appView, nullability);
}
}