package com.oracle.svm.hosted.meta;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import org.graalvm.word.WordBase;
import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider;
import com.oracle.graal.pointsto.infrastructure.WrappedJavaType;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.meta.SharedType;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
public abstract class HostedType implements SharedType, WrappedJavaType, Comparable<HostedType>, OriginalClassProvider {
protected final HostedUniverse universe;
protected final AnalysisType wrapped;
private final JavaKind kind;
private final JavaKind storageKind;
private final HostedClass superClass;
private final HostedInterface[] interfaces;
private HostedType enclosingType;
protected HostedArrayClass arrayType;
protected HostedType[] subTypes;
protected HostedField[] staticFields;
protected HostedMethod[] vtable;
protected int typeID;
protected HostedType uniqueConcreteImplementation;
protected HostedMethod[] allDeclaredMethods;
protected short typeCheckStart;
protected short typeCheckRange;
protected short typeCheckSlot;
protected short[] typeCheckSlots;
protected HostedType strengthenStampType;
private final boolean isCloneable;
public HostedType(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces, boolean isCloneable) {
this.universe = universe;
this.wrapped = wrapped;
this.kind = kind;
this.storageKind = storageKind;
this.superClass = superClass;
this.interfaces = interfaces;
this.typeID = -1;
this.isCloneable = isCloneable;
}
public HostedType getStrengthenStampType() {
return strengthenStampType;
}
public HostedType[] getSubTypes() {
assert subTypes != null;
return subTypes;
}
public HostedMethod[] getVTable() {
assert vtable != null;
return vtable;
}
public int getTypeID() {
assert typeID != -1;
return typeID;
}
public void setTypeCheckRange(short typeCheckStart, short typeCheckRange) {
this.typeCheckStart = typeCheckStart;
this.typeCheckRange = typeCheckRange;
}
public void setTypeCheckSlot(short typeCheckSlot) {
this.typeCheckSlot = typeCheckSlot;
}
public void setTypeCheckSlots(short[] typeCheckSlots) {
this.typeCheckSlots = typeCheckSlots;
}
public short getTypeCheckStart() {
return typeCheckStart;
}
public short getTypeCheckRange() {
return typeCheckRange;
}
public short getTypeCheckSlot() {
return typeCheckSlot;
}
public short[] getTypeCheckSlots() {
assert typeCheckSlots != null;
return typeCheckSlots;
}
public boolean isWordType() {
return kind != storageKind;
}
public HostedMethod[] getAllDeclaredMethods() {
assert allDeclaredMethods != null : "not initialized yet";
return allDeclaredMethods;
}
public HostedType getUniqueConcreteImplementation() {
return uniqueConcreteImplementation;
}
@Override
public DynamicHub getHub() {
return universe.hostVM().dynamicHub(wrapped);
}
@Override
public AnalysisType getWrapped() {
return wrapped;
}
public boolean isInstantiated() {
return wrapped.isInstantiated();
}
@Override
public final String getName() {
return wrapped.getName();
}
@Override
public final JavaKind getJavaKind() {
return kind;
}
@Override
public final JavaKind getStorageKind() {
return storageKind;
}
@Override
public final ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
return this;
}
@Override
public final boolean hasFinalizer() {
return false;
}
@Override
public final AssumptionResult<Boolean> hasFinalizableSubclass() {
return new AssumptionResult<>(false);
}
@Override
public final boolean isInitialized() {
return wrapped.isInitialized();
}
@Override
public void initialize() {
wrapped.initialize();
}
@Override
public final HostedArrayClass getArrayClass() {
return arrayType;
}
public HostedType getArrayClass(int dimension) {
HostedType result = this;
for (int i = 0; i < dimension; i++) {
result = result.arrayType;
if (result == null) {
return null;
}
}
return result;
}
@Override
public abstract HostedField[] getInstanceFields(boolean includeSuperclasses);
@Override
public ResolvedJavaField[] getStaticFields() {
assert staticFields != null;
return staticFields;
}
@Override
public final HostedClass getSuperclass() {
return superClass;
}
@Override
public final HostedInterface[] getInterfaces() {
return interfaces;
}
@Override
public abstract HostedType getComponentType();
public abstract HostedType getBaseType();
public abstract int getArrayDimension();
@Override
public AssumptionResult<ResolvedJavaType> findLeafConcreteSubtype() {
ResolvedJavaType result = getSingleImplementor();
if (result == null) {
return null;
} else {
return new AssumptionResult<>(result);
}
}
@Override
public HostedType getSingleImplementor() {
return uniqueConcreteImplementation;
}
@Override
public final boolean isAssignableFrom(ResolvedJavaType other) {
return wrapped.isAssignableFrom(((HostedType) other).wrapped);
}
@Override
public final ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) {
return universe.lookup(wrapped.findLeastCommonAncestor(((HostedType) otherType).wrapped));
}
@Override
public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod m, ResolvedJavaType ct) {
HostedMethod method = (HostedMethod) m;
HostedType callerType = (HostedType) ct;
if (isWordType()) {
return wrappedResolveMethod(method, callerType);
}
ResolvedJavaMethod found = SharedType.super.resolveConcreteMethod(method, callerType);
assert isAbstract() || (found == null || checkWrappedResolveMethod(method, found, callerType));
return found;
}
private boolean checkWrappedResolveMethod(HostedMethod method, ResolvedJavaMethod found, HostedType callerType) {
ResolvedJavaMethod wrappedMethod = wrappedResolveMethod(method, callerType);
return wrappedMethod == null || found.equals(wrappedMethod);
}
private ResolvedJavaMethod wrappedResolveMethod(HostedMethod method, HostedType callerType) {
AnalysisMethod orig = wrapped.resolveConcreteMethod(method.wrapped, callerType.wrapped);
ResolvedJavaMethod result = orig == null ? null : universe.lookup(orig);
if (result != null && !isWordType() && !Arrays.asList(method.getImplementations()).contains(result)) {
result = null;
}
return result;
}
@Override
public final int getModifiers() {
return wrapped.getModifiers();
}
@Override
public final boolean isInstance(JavaConstant obj) {
assert universe.lookup(obj) == obj : "constant should not have analysis-universe dependent value";
return wrapped.isInstance(obj);
}
@Override
public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
return null;
}
@Override
public Annotation[] getAnnotations() {
return wrapped.getAnnotations();
}
@Override
public Annotation[] getDeclaredAnnotations() {
return wrapped.getDeclaredAnnotations();
}
@Override
public final <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return wrapped.getAnnotation(annotationClass);
}
@Override
public String getSourceFileName() {
return wrapped.getSourceFileName();
}
@Override
public String toString() {
return "HostedType<" + toJavaName(true) + " " + wrapped.toString() + ">";
}
@Override
public boolean isLocal() {
return wrapped.isLocal();
}
@Override
public boolean isMember() {
return wrapped.isLocal();
}
@Override
public HostedType getEnclosingType() {
return enclosingType;
}
@Override
public HostedMethod[] getDeclaredConstructors() {
return universe.lookup(wrapped.getDeclaredConstructors());
}
@Override
public HostedMethod[] getDeclaredMethods() {
return universe.lookup(wrapped.getDeclaredMethods());
}
@Override
public ResolvedJavaMethod getClassInitializer() {
return universe.lookup(wrapped.getClassInitializer());
}
@Override
public boolean isLinked() {
return wrapped.isLinked();
}
@Override
public void link() {
wrapped.link();
}
@Override
public boolean hasDefaultMethods() {
return wrapped.hasDefaultMethods();
}
@Override
public boolean declaresDefaultMethods() {
return wrapped.declaresDefaultMethods();
}
@Override
public boolean isCloneableWithAllocation() {
return isCloneable;
}
@Override
public ResolvedJavaType getHostClass() {
return universe.lookup(wrapped.getHostClass());
}
public void setEnclosingType(HostedType enclosingType) {
this.enclosingType = enclosingType;
}
@Override
public Class<?> getJavaClass() {
return OriginalClassProvider.getJavaClass(universe.getSnippetReflection(), wrapped);
}
@Override
public int compareTo(HostedType other) {
if (this.equals(other)) {
return 0;
}
if (this.getClass().equals(other.getClass())) {
return compareToEqualClass(other);
}
int result = this.ordinal() - other.ordinal();
assert result != 0 : "Types not distinguishable: " + this + ", " + other;
return result;
}
int compareToEqualClass(HostedType other) {
assert getClass().equals(other.getClass());
return getName().compareTo(other.getName());
}
private int ordinal() {
if (isInterface()) {
return 4;
} else if (isArray()) {
return 3;
} else if (isInstanceClass()) {
return 2;
} else if (getJavaKind() != JavaKind.Object) {
return 1;
} else {
throw shouldNotReachHere();
}
}
}