package checkers.oigj;
import java.util.Collections;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.Tree;
import checkers.basetype.BaseTypeChecker;
import checkers.oigj.quals.*;
import checkers.quals.TypeQualifiers;
import checkers.types.AnnotatedTypeMirror;
import checkers.types.QualifierHierarchy;
import checkers.types.TypeHierarchy;
import checkers.types.AnnotatedTypeMirror.AnnotatedDeclaredType;
import checkers.util.AnnotationUtils;
import checkers.util.InternalUtils;
@TypeQualifiers({ ReadOnly.class, Mutable.class, Immutable.class, I.class,
AssignsFields.class, OIGJMutabilityBottom.class })
public class ImmutabilitySubchecker extends BaseTypeChecker {
/** Supported annotations for IGJ. Used for subtyping rules. **/
protected AnnotationMirror READONLY, MUTABLE, IMMUTABLE, I, ASSIGNS_FIELDS, BOTTOM_QUAL;
@Override
public synchronized void init(ProcessingEnvironment env) {
AnnotationUtils annoFactory = AnnotationUtils.getInstance(env);
READONLY = annoFactory.fromClass(ReadOnly.class);
MUTABLE = annoFactory.fromClass(Mutable.class);
IMMUTABLE = annoFactory.fromClass(Immutable.class);
I = annoFactory.fromClass(I.class);
ASSIGNS_FIELDS = annoFactory.fromClass(AssignsFields.class);
BOTTOM_QUAL = annoFactory.fromClass(OIGJMutabilityBottom.class);
super.init(env);
}
//
// OIGJ Rule 6. Same-class subtype definition
// Let C<I, X_1, ..., X_n> be a class. Type S = C<J, S_1, ..., S_n>
// is a subtype of T = C<J', T_1, ... T_n> witten as S<= T, iff J <= J' and
// for i = 1, ..., n, either S_i = T_i or
// (Immutable <= J' and S_i <= T_i and CoVariant(X_i, C))
//
@Override
protected TypeHierarchy createTypeHierarchy() {
return new OIGJImmutabilityQualifierHierarchy(getQualifierHierarchy());
}
private final class OIGJImmutabilityQualifierHierarchy extends TypeHierarchy {
public OIGJImmutabilityQualifierHierarchy(
QualifierHierarchy qualifierHierarchy) {
super(qualifierHierarchy);
}
/**
* OIGJ Rule 6. <b>Same-class subtype definition</b>
*/
// TODO: Handle CoVariant(X_i, C)
@Override
protected boolean isSubtypeTypeArguments(AnnotatedDeclaredType rhs, AnnotatedDeclaredType lhs) {
if (lhs.hasAnnotation(MUTABLE))
return super.isSubtypeTypeArguments(rhs, lhs);
if (!lhs.getTypeArguments().isEmpty()
&& !rhs.getTypeArguments().isEmpty()) {
assert lhs.getTypeArguments().size() == rhs.getTypeArguments().size();
for (int i = 0; i < lhs.getTypeArguments().size(); ++i) {
if (!isSubtype(rhs.getTypeArguments().get(i), lhs.getTypeArguments().get(i)))
return false;
}
}
return true;
}
}
//
// OIGJ Rule 2. Field assignment
// Field assignment o.f = ... is legal iff
// (i) I(o) <= AssignsFields or f is annotated as @Assignable, and
// (ii) o = this or the type of f does not contain the owner Dominator
// or Modifier
//
@Override
public boolean isAssignable(AnnotatedTypeMirror varType,
AnnotatedTypeMirror receiverType, Tree varTree) {
if (!(varTree instanceof ExpressionTree))
return true;
Element varElement = InternalUtils.symbol(varTree);
if (varElement != null && varElement.getAnnotation(Assignable.class) != null)
return true;
if (getQualifierHierarchy().isSubtype(
receiverType.getAnnotations(),
Collections.singleton(ASSIGNS_FIELDS)))
return true;
return false;
}
}