package checkers.jimmu;
import checkers.basetype.BaseTypeChecker;
import checkers.basetype.BaseTypeVisitor;
import checkers.jimmu.quals.*;
import checkers.quals.TypeQualifiers;
import checkers.types.AnnotatedTypeFactory;
import checkers.types.AnnotatedTypeMirror;
import checkers.types.AnnotatedTypeMirror.AnnotatedNullType;
import checkers.types.QualifierHierarchy;
import checkers.util.AnnotationUtils;
import checkers.util.GraphQualifierHierarchy;
import com.sun.source.tree.CompilationUnitTree;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.tools.Diagnostic.Kind;
/**
* The main checker class for JimmuChecker.
*
* @author saf
*/
@TypeQualifiers({
/* Class qualifiers */
ImmutableClass.class, MutableClass.class,
/* Object and type qualifiers */
Bottom.class, Mutable.class, Immutable.class, Myaccess.class,
/* Method & constructor qualifiers */
ReadOnly.class, Anonymous.class,
/* Field qualifiers */
Rep.class, Peer.class, World.class, OwnedBy.class, AnyOwner.class,
/* Annotations for tracking references to this */
This.class, NotThis.class, MaybeThis.class,
/* Annotations for safe parameters */
Safe.class
})
public class JimmuChecker extends BaseTypeChecker {
protected AnnotationUtils annotationFactory;
protected JimmuVisitorState state;
protected Boolean lenientUpcasting;
public AnnotationMirror BOTTOM, IMMUTABLE, MUTABLE, MYACCESS,
READONLY, REP, PEER, WORLD, OWNEDBY,
ANONYMOUS, IMMUTABLE_CLASS,
THIS, NOT_THIS, MAYBE_THIS,
SAFE, ANYOWNER;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
annotationFactory = AnnotationUtils.getInstance(processingEnv);
BOTTOM = annotationFactory.fromClass(Bottom.class);
IMMUTABLE = annotationFactory.fromClass(Immutable.class);
MUTABLE = annotationFactory.fromClass(Mutable.class);
MYACCESS = annotationFactory.fromClass(Myaccess.class);
READONLY = annotationFactory.fromClass(ReadOnly.class);
REP = annotationFactory.fromClass(Rep.class);
PEER = annotationFactory.fromClass(Peer.class);
WORLD = annotationFactory.fromClass(World.class);
OWNEDBY = annotationFactory.fromClass(OwnedBy.class);
ANYOWNER = annotationFactory.fromClass(AnyOwner.class);
ANONYMOUS = annotationFactory.fromClass(Anonymous.class);
IMMUTABLE_CLASS = annotationFactory.fromClass(ImmutableClass.class);
THIS = annotationFactory.fromClass(This.class);
NOT_THIS = annotationFactory.fromClass(NotThis.class);
MAYBE_THIS = annotationFactory.fromClass(MaybeThis.class);
SAFE = annotationFactory.fromClass(Safe.class);
state = new JimmuVisitorState();
lenientUpcasting = false;
Map<String, String> options = processingEnv.getOptions();
if (options.containsKey("allow.upcast")) {
String value = options.get("allow.upcast");
if (value == null || value.equals("true") || value.equals("1")) {
lenientUpcasting = true;
}
}
super.init(processingEnv);
}
@Override
public AnnotatedTypeFactory createFactory(CompilationUnitTree root) {
return new JimmuAnnotatedTypeFactory(this, root, state);
}
@Override
protected BaseTypeVisitor<?, ?> createSourceVisitor(CompilationUnitTree root) {
try {
return new JimmuVisitor(this, root, state);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public AnnotationUtils getAnnotationFactory() {
return annotationFactory;
}
public JimmuVisitorState getState() {
return state;
}
/**
* Issue a note to the error stream.
*/
public void note(Object source, String messageKey, Object... args) {
String msg = messageKey;
if (messages.containsKey(messageKey)) {
msg = messages.getProperty(messageKey);
}
if (source != null) {
message(Kind.OTHER, source, String.format(msg, args));
} else {
message(Kind.NOTE, null, String.format(msg, args));
}
}
@Override
public Set<String> getSupportedOptions() {
return Collections.singleton("allow.upcast");
}
public Boolean allowUpcast() {
return lenientUpcasting;
}
public static class JimmuQualifierHierarchy extends GraphQualifierHierarchy {
public JimmuQualifierHierarchy(GraphQualifierHierarchy h) {
super(h);
}
/**
* Every annotation from LHS must have a match in RHS.
*
* This is because different sets of annotations cover various
* features of an element.
*/
@Override
public boolean isSubtype(Collection<AnnotationMirror> rhs, Collection<AnnotationMirror> lhs) {
Collection<AnnotationMirror> unmatched = new HashSet<AnnotationMirror>(lhs);
for (AnnotationMirror al : lhs) {
if (!AnnotationUtils.isTypeAnnotation(al)) {
unmatched.remove(al);
} else {
for (AnnotationMirror ar : rhs) {
if (isSubtype(ar, al)) {
unmatched.remove(al);
}
}
}
}
return unmatched.isEmpty();
}
}
/**
* An AnnotatedNullTypeMirror may be a subtype of anything.
*/
@Override
public boolean isSubtype(AnnotatedTypeMirror rhs, AnnotatedTypeMirror lhs) {
if (rhs instanceof AnnotatedNullType) {
return true;
} else {
return super.isSubtype(rhs, lhs);
}
}
@Override
protected QualifierHierarchy createQualifierHierarchy() {
QualifierHierarchy hierarchy = super.createQualifierHierarchy();
return new JimmuQualifierHierarchy((GraphQualifierHierarchy) hierarchy);
}
}