package org.graalvm.compiler.core.common.type;
import java.util.AbstractList;
import java.util.Objects;
import java.util.RandomAccess;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
public abstract class AbstractObjectStamp extends AbstractPointerStamp {
private final ResolvedJavaType type;
private final boolean exactType;
private final boolean alwaysArray;
protected AbstractObjectStamp(ResolvedJavaType type, boolean exactType, boolean nonNull, boolean alwaysNull, boolean alwaysArray) {
super(nonNull, alwaysNull);
this.type = type;
this.exactType = exactType;
this.alwaysArray = alwaysArray || (type != null && type.isArray());
}
@Override
public void accept(Visitor v) {
super.accept(v);
v.visitObject(type);
v.visitBoolean(exactType);
v.visitBoolean(alwaysArray);
}
protected abstract AbstractObjectStamp copyWith(ResolvedJavaType newType, boolean newExactType, boolean newNonNull, boolean newAlwaysNull, boolean newAlwaysArray);
@Override
protected final AbstractPointerStamp copyWith(boolean newNonNull, boolean newAlwaysNull) {
return copyWith(type, exactType, newNonNull, newAlwaysNull, alwaysArray);
}
@Override
public Stamp unrestricted() {
return copyWith(null, false, false, false, false);
}
@Override
public Stamp empty() {
return copyWith(null, true, true, false, false);
}
@Override
public Stamp constant(Constant c, MetaAccessProvider meta) {
JavaConstant jc = (JavaConstant) c;
ResolvedJavaType constType = jc.isNull() ? null : meta.lookupJavaType(jc);
return copyWith(constType, jc.isNonNull(), jc.isNonNull(), jc.isNull(), false);
}
@Override
public boolean hasValues() {
return !exactType || (type != null && (isConcreteType(type)));
}
@Override
public JavaKind getStackKind() {
return JavaKind.Object;
}
@Override
public ResolvedJavaType javaType(MetaAccessProvider metaAccess) {
if (type != null) {
return type;
}
return metaAccess.lookupJavaType(Object.class);
}
public ResolvedJavaType type() {
return type;
}
public boolean isExactType() {
return exactType && type != null;
}
public boolean isAlwaysArray() {
return alwaysArray;
}
public AbstractObjectStamp asAlwaysArray() {
return copyWith(type, exactType, nonNull(), alwaysNull(), true);
}
protected void appendString(StringBuilder str) {
if (this.isEmpty()) {
str.append(" empty");
} else {
str.append(nonNull() ? "!" : "").append(exactType ? "#" : "").append(alwaysArray ? "[" : "").append(' ').append(type == null ? "-" : type.getName()).append(alwaysNull() ? " NULL" : "");
}
}
@Override
public Stamp meet(Stamp otherStamp) {
if (this == otherStamp) {
return this;
}
AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
if (isEmpty()) {
return other;
} else if (other.isEmpty()) {
return this;
}
ResolvedJavaType meetType;
boolean meetExactType;
boolean meetNonNull;
boolean meetAlwaysNull;
boolean meetAlwaysArray;
if (other.alwaysNull()) {
meetType = type();
meetExactType = exactType;
meetNonNull = false;
meetAlwaysNull = alwaysNull();
meetAlwaysArray = alwaysArray;
} else if (alwaysNull()) {
meetType = other.type();
meetExactType = other.exactType;
meetNonNull = false;
meetAlwaysNull = other.alwaysNull();
meetAlwaysArray = other.alwaysArray;
} else {
meetType = meetTypes(type(), other.type());
meetExactType = exactType && other.exactType;
if (meetExactType && type != null && other.type != null) {
meetExactType = Objects.equals(meetType, type) && Objects.equals(meetType, other.type);
}
meetNonNull = nonNull() && other.nonNull();
meetAlwaysNull = false;
meetAlwaysArray = this.alwaysArray && other.alwaysArray;
}
if (Objects.equals(meetType, type) && meetExactType == exactType && meetNonNull == nonNull() && meetAlwaysNull == alwaysNull() && meetAlwaysArray == alwaysArray) {
return this;
} else if (Objects.equals(meetType, other.type) && meetExactType == other.exactType && meetNonNull == other.nonNull() && meetAlwaysNull == other.alwaysNull() &&
meetAlwaysArray == other.alwaysArray) {
return other;
} else {
return copyWith(meetType, meetExactType, meetNonNull, meetAlwaysNull, meetAlwaysArray);
}
}
@Override
public Stamp join(Stamp otherStamp) {
return join0(otherStamp, false);
}
@Override
public Stamp improveWith(Stamp other) {
return join0(other, true);
}
private Stamp join0(Stamp otherStamp, boolean improve) {
if (this == otherStamp) {
return this;
}
AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
if (isEmpty()) {
return this;
} else if (other.isEmpty()) {
return other;
}
ResolvedJavaType joinType;
boolean joinAlwaysNull = alwaysNull() || other.alwaysNull();
boolean joinNonNull = nonNull() || other.nonNull();
boolean joinExactType = exactType || other.exactType;
boolean joinAlwaysArray = alwaysArray || other.alwaysArray;
if (Objects.equals(type, other.type)) {
joinType = type;
} else if (type == null) {
joinType = other.type;
} else if (other.type == null) {
joinType = type;
} else {
if (type.isAssignableFrom(other.type)) {
joinType = other.type;
if (exactType) {
joinAlwaysNull = true;
}
} else if (other.type.isAssignableFrom(type)) {
joinType = type;
if (other.exactType) {
joinAlwaysNull = true;
}
} else {
if (improve) {
joinType = type;
joinExactType = exactType;
} else {
joinType = null;
}
if (joinExactType || (!isInterfaceOrArrayOfInterface(type) && !isInterfaceOrArrayOfInterface(other.type))) {
joinAlwaysNull = true;
}
}
}
if (joinAlwaysArray && joinType != null && !joinType.isArray() && (joinExactType || !joinType.isJavaLangObject())) {
joinAlwaysNull = true;
}
if (joinAlwaysNull) {
joinType = null;
joinExactType = false;
joinAlwaysArray = false;
}
if (joinExactType && joinType == null) {
return empty();
}
if (joinAlwaysNull && joinNonNull) {
return empty();
} else if (joinExactType && !isConcreteType(joinType)) {
return empty();
}
if (Objects.equals(joinType, type) && joinExactType == exactType && joinNonNull == nonNull() && joinAlwaysNull == alwaysNull() && joinAlwaysArray == alwaysArray) {
return this;
} else if (Objects.equals(joinType, other.type) && joinExactType == other.exactType && joinNonNull == other.nonNull() && joinAlwaysNull == other.alwaysNull() &&
joinAlwaysArray == other.alwaysArray) {
return other;
} else {
return copyWith(joinType, joinExactType, joinNonNull, joinAlwaysNull, joinAlwaysArray);
}
}
private static boolean isInterfaceOrArrayOfInterface(ResolvedJavaType t) {
return t.isInterface() || (t.isArray() && t.getElementalType().isInterface());
}
public static boolean isConcreteType(ResolvedJavaType type) {
return !(type.isAbstract() && !type.isArray());
}
private static ResolvedJavaType meetTypes(ResolvedJavaType a, ResolvedJavaType b) {
if (Objects.equals(a, b)) {
return a;
} else if (a == null || b == null) {
return null;
} else {
int hashA = a.getName().hashCode();
int hashB = b.getName().hashCode();
if (hashA < hashB) {
return meetOrderedNonNullTypes(a, b);
} else if (hashB < hashA) {
return meetOrderedNonNullTypes(b, a);
} else {
int diff = a.getName().compareTo(b.getName());
if (diff <= 0) {
return meetOrderedNonNullTypes(a, b);
} else {
return meetOrderedNonNullTypes(b, a);
}
}
}
}
private static ResolvedJavaType meetOrderedNonNullTypes(ResolvedJavaType a, ResolvedJavaType b) {
ResolvedJavaType result = a.findLeastCommonAncestor(b);
if (result.isJavaLangObject() && a.isInterface() && b.isInterface()) {
ResolvedJavaType[] interfacesA = a.getInterfaces();
ResolvedJavaType[] interfacesB = b.getInterfaces();
for (int i = 0; i < interfacesA.length; ++i) {
ResolvedJavaType interface1 = interfacesA[i];
for (int j = 0; j < interfacesB.length; ++j) {
ResolvedJavaType interface2 = interfacesB[j];
ResolvedJavaType leastCommon = meetTypes(interface1, interface2);
if (leastCommon.isInterface()) {
return leastCommon;
}
}
}
}
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + super.hashCode();
result = prime * result + (exactType ? 1231 : 1237);
result = prime * result + (alwaysArray ? 1231 : 1237);
result = prime * result + ((type == null || type.isJavaLangObject()) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AbstractObjectStamp other = (AbstractObjectStamp) obj;
if (exactType != other.exactType) {
return false;
}
if (alwaysArray != other.alwaysArray) {
return false;
}
if (type == null) {
if (other.type != null && !other.type.isJavaLangObject()) {
return false;
}
} else if (other.type == null) {
if (type != null && !type.isJavaLangObject()) {
return false;
}
} else if (!type.equals(other.type)) {
return false;
}
return super.equals(other);
}
}