View Javadoc

1   /*
2    * Created on 14.07.2004
3    */
4   package net.sourceforge.pmd.dfa.variableaccess;
5   
6   import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
7   import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
8   import net.sourceforge.pmd.ast.ASTFormalParameter;
9   import net.sourceforge.pmd.ast.ASTMethodDeclaration;
10  import net.sourceforge.pmd.ast.ASTVariableInitializer;
11  import net.sourceforge.pmd.ast.JavaParserVisitorAdapter;
12  import net.sourceforge.pmd.ast.SimpleNode;
13  import net.sourceforge.pmd.dfa.IDataFlowNode;
14  import net.sourceforge.pmd.dfa.StartOrEndDataFlowNode;
15  import net.sourceforge.pmd.symboltable.NameOccurrence;
16  import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
17  
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Set;
23  
24  /**
25   * @author raik, Sven Jacob
26   *         <p/>
27   *         Searches for special nodes and computes based on the sequence, the type of
28   *         access of a variable.
29   */
30  public class VariableAccessVisitor extends JavaParserVisitorAdapter {
31  
32      public void compute(ASTMethodDeclaration node) {
33          if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
34              this.computeNow(node);
35          }
36      }
37  
38      public void compute(ASTConstructorDeclaration node) {
39          this.computeNow(node);
40      }
41  
42      private void computeNow(SimpleNode node) {
43          IDataFlowNode inode = node.getDataFlowNode();
44  
45          List<VariableAccess> undefinitions = markUsages(inode);
46  
47          // all variables are first in state undefinition 
48          IDataFlowNode firstINode = inode.getFlow().get(0);
49          firstINode.setVariableAccess(undefinitions);
50  
51          // all variables are getting undefined when leaving scope
52          IDataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
53          lastINode.setVariableAccess(undefinitions);
54      }
55  
56      private List<VariableAccess> markUsages(IDataFlowNode inode) {
57          // undefinitions was once a field... seems like it works fine as a local
58          List<VariableAccess> undefinitions = new ArrayList<VariableAccess>();
59          Set<Map<VariableNameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
60          for (Map<VariableNameDeclaration, List<NameOccurrence>> declarations: variableDeclarations) {
61              for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry: declarations.entrySet()) {
62                  VariableNameDeclaration vnd = entry.getKey();
63  
64                  if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
65                      // no definition/undefinition/references for parameters
66                      continue;
67                  } else if (vnd.getAccessNodeParent().getFirstChildOfType(ASTVariableInitializer.class) != null) {
68                      // add definition for initialized variables
69                      addVariableAccess(
70                              vnd.getNode(), 
71                              new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()), 
72                              inode.getFlow());                    
73                  }
74                  undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
75  
76                  for (NameOccurrence occurrence: entry.getValue()) {
77                      addAccess(occurrence, inode);
78                  }
79              }
80          }
81          return undefinitions;
82      }
83  
84      private Set<Map<VariableNameDeclaration, List<NameOccurrence>>> collectDeclarations(IDataFlowNode inode) {
85          Set<Map<VariableNameDeclaration, List<NameOccurrence>>> decls = new HashSet<Map<VariableNameDeclaration, List<NameOccurrence>>>();
86          Map<VariableNameDeclaration, List<NameOccurrence>> varDecls;
87          for (int i = 0; i < inode.getFlow().size(); i++) {
88              IDataFlowNode n = inode.getFlow().get(i);
89              if (n instanceof StartOrEndDataFlowNode) {
90                  continue;
91              }
92              varDecls = n.getSimpleNode().getScope().getVariableDeclarations();
93              if (!decls.contains(varDecls)) {
94                  decls.add(varDecls);
95              }
96          }
97          return decls;
98      }
99  
100     private void addAccess(NameOccurrence occurrence, IDataFlowNode inode) {
101         if (occurrence.isOnLeftHandSide()) {
102             this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence.getImage()), inode.getFlow());
103         } else if (occurrence.isOnRightHandSide() || (!occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide())) {
104             this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence.getImage()), inode.getFlow());
105         }
106     }
107 
108     /**
109      * Adds a VariableAccess to a dataflow node.
110      * @param node location of the access of a variable
111      * @param va variable access to add
112      * @param flow dataflownodes that can contain the node. 
113      */
114     private void addVariableAccess(SimpleNode node, VariableAccess va, List flow) {
115         // backwards to find the right inode (not a method declaration) 
116         for (int i = flow.size()-1; i > 0; i--) { 
117             IDataFlowNode inode = (IDataFlowNode) flow.get(i);
118             if (inode.getSimpleNode() == null) {
119                 continue;
120             }
121 
122             List<? extends SimpleNode> children = inode.getSimpleNode().findChildrenOfType(node.getClass());
123             for (SimpleNode n: children) {
124                 if (node.equals(n)) { 
125                     List<VariableAccess> v = new ArrayList<VariableAccess>();
126                     v.add(va);
127                     inode.setVariableAccess(v);     
128                     return;
129                 }
130             }
131         }
132     }
133 
134 }