package checkers.jimmu; import checkers.jimmu.JimmuVisitor.Owner; import checkers.types.AnnotatedTypeMirror; import checkers.types.AnnotatedTypeMirror.*; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Stack; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; /** * An object of the JimmuVisitorState class holds additional information during the * source code traversal by JimmuVisitor. * * @author saf */ public class JimmuVisitorState { protected JimmuAnnotatedTypeFactory atypeFactory; protected Stack<AnnotatedDeclaredType> enclosingClassStack; protected Stack<AnnotatedExecutableType> enclosingMethodStack; /* Current class annotated type */ protected AnnotatedDeclaredType currentClass; /* Current method annotated type */ protected AnnotatedExecutableType currentMethod; /* Current method. Used in Flow context only */ protected MethodTree currentMethodFlow; protected Stack<MethodTree> enclosingMethodsFlow; /* Block -- represents the local variables declared inside a block */ protected static class Block { private Map<String, AnnotatedTypeMirror> vars; public Block() { vars = new HashMap<String, AnnotatedTypeMirror>(); } public AnnotatedTypeMirror get(String s) { return vars.get(s); } public void add(String s, AnnotatedTypeMirror m) { vars.put(s, m); } } protected LinkedList<Block> blocks; /* The type for the receiver of the current MethodInvocation. * Needed to infer type of paramaters in JimmuVisitor. */ protected AnnotatedTypeMirror invocationReceiver; /* Owner of the current invocation receiver */ protected JimmuVisitor.Owner invocationReceiverOwner; protected MethodInvocationTree currentInvocation; /* Map of implicit annotations that the Factory put on methods */ protected Map<ExecutableElement, AnnotationMirror> implicitAnnotations; public JimmuVisitorState() { implicitAnnotations = new HashMap<ExecutableElement, AnnotationMirror>(); enclosingClassStack = new Stack<AnnotatedDeclaredType>(); enclosingMethodStack = new Stack<AnnotatedExecutableType>(); enclosingMethodsFlow = new Stack<MethodTree>(); blocks = new LinkedList<Block>(); } public void setFactory(JimmuAnnotatedTypeFactory factory) { this.atypeFactory = factory; } public void enterClass(ClassTree tree) { if (currentClass != null) { enclosingClassStack.push(currentClass); } currentClass = atypeFactory.getAnnotatedType(tree); } public void leaveClass() { if (!enclosingClassStack.isEmpty()) { currentClass = enclosingClassStack.pop(); } else { currentClass = null; } } public AnnotatedTypeMirror getCurrentClass() { return currentClass; } public Boolean isCurrentClass(AnnotationMirror ann) { return currentClass != null && currentClass.hasAnnotation(ann); } public String getCurrentClassName() { return currentClass.getElement().getSimpleName().toString(); } public void enterMethod(MethodTree tree) { if (currentMethod != null) { enclosingMethodStack.push(currentMethod); } currentMethod = atypeFactory.getAnnotatedType(tree); } public void leaveMethod() { if (!enclosingMethodStack.isEmpty()) { currentMethod = enclosingMethodStack.pop(); } else { currentMethod = null; } } public AnnotatedExecutableType getCurrentMethod() { return currentMethod; } public Boolean isCurrentMethod(AnnotationMirror ann) { /* * currentMethod may be null when doing Flow analysis, but * the result of this method is meaningless for flow-inferred properties. */ return currentMethod != null && currentMethod.hasAnnotation(ann); } public Boolean isCurrentMethodReceiver(AnnotationMirror ann) { return currentMethod.getReceiverType().hasAnnotation(ann); } public String getCurrentMethodName() { return currentMethod.getElement().getSimpleName().toString(); } public void addImplicitAnnotation(ExecutableElement el, AnnotationMirror an) { implicitAnnotations.put(el, an); } public boolean isImplicitlyAnnotated(ExecutableElement el) { return implicitAnnotations.containsKey(el); } public boolean inImplicitlyAnnotatedMethod() { return isImplicitlyAnnotated(currentMethod.getElement()); } public AnnotationMirror getImplicitAnnotation(ExecutableElement el) { return implicitAnnotations.get(el); } public Boolean inConstructor() { return currentMethod != null ? currentMethod.getElement().getKind().equals(ElementKind.CONSTRUCTOR) : (currentMethodFlow.getReturnType() == null); } public void setCurrentInvocation(Tree t) { if (t.getKind() == Tree.Kind.METHOD_INVOCATION) { MethodInvocationTree mt = (MethodInvocationTree) t; currentInvocation = mt; invocationReceiver = atypeFactory.getReceiver(mt); } else if (t.getKind() == Tree.Kind.NEW_CLASS) { NewClassTree nt = (NewClassTree) t; currentInvocation = null; invocationReceiver = atypeFactory.fromTypeTree(nt.getIdentifier()); } if (invocationReceiver != null) { try { invocationReceiverOwner = new JimmuVisitor.Owner(invocationReceiver.getElement(), atypeFactory); } catch (Owner.OwnerDescriptionError e) { /* Ignore; should have already been reported */ invocationReceiverOwner = null; } } else { invocationReceiverOwner = null; } } public Boolean isReceiver(AnnotationMirror m) { return invocationReceiver == null ? false : invocationReceiver.hasAnnotation(m); } public Owner getReceiverOwner() { return invocationReceiverOwner; } public MethodInvocationTree getCurrentInvocation() { return currentInvocation; } public void enterMethodFlow(MethodTree tree) { if (currentMethodFlow != null) { enclosingMethodsFlow.push(currentMethodFlow); } currentMethodFlow = tree; } public void leaveMethodFlow() { if (!enclosingMethodsFlow.isEmpty()) { currentMethodFlow = enclosingMethodsFlow.pop(); } else { currentMethodFlow = null; } } public void enterBlock() { blocks.push(new Block()); } public void leaveBlock() { blocks.pop(); } public void addVariable(String s, AnnotatedTypeMirror m) { blocks.peek().add(s, m); } public void shadowVariable(String s) { /* * If a variable is shadowed by a field, we put a dummy entry * into the first block's vars which acts as if the variable did not exist. */ if (!blocks.isEmpty()) { blocks.peek().add(s, null); } } public AnnotatedTypeMirror localVariable(String s) { Iterator<Block> it = blocks.iterator(); while (it.hasNext()) { Block b = it.next(); AnnotatedTypeMirror m = b.get(s); if (m != null) { return m; } } return null; } }