View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.plsql.dfa;
5   
6   import java.util.ArrayList;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import net.sourceforge.pmd.lang.ast.Node;
13  import net.sourceforge.pmd.lang.dfa.DataFlowNode;
14  import net.sourceforge.pmd.lang.dfa.StartOrEndDataFlowNode;
15  import net.sourceforge.pmd.lang.dfa.VariableAccess;
16  import net.sourceforge.pmd.lang.plsql.ast.ASTCompoundTriggerBlock;
17  import net.sourceforge.pmd.lang.plsql.ast.ASTFormalParameter;
18  import net.sourceforge.pmd.lang.plsql.ast.ASTMethodDeclaration;
19  import net.sourceforge.pmd.lang.plsql.ast.ASTPackageBody;
20  import net.sourceforge.pmd.lang.plsql.ast.ASTProgramUnit;
21  import net.sourceforge.pmd.lang.plsql.ast.ASTTriggerTimingPointSection;
22  import net.sourceforge.pmd.lang.plsql.ast.ASTTriggerUnit;
23  import net.sourceforge.pmd.lang.plsql.ast.ASTTypeMethod;
24  import net.sourceforge.pmd.lang.plsql.ast.ASTVariableOrConstantInitializer;
25  import net.sourceforge.pmd.lang.plsql.ast.PLSQLNode;
26  import net.sourceforge.pmd.lang.plsql.ast.PLSQLParserVisitorAdapter;
27  import net.sourceforge.pmd.lang.plsql.symboltable.PLSQLNameOccurrence;
28  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
29  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
30  
31  /**
32   * @author raik, Sven Jacob
33   *         <p/>
34   *         Searches for special nodes and computes based on the sequence, the type of
35   *         access of a variable.
36   */
37  public class VariableAccessVisitor extends PLSQLParserVisitorAdapter {
38  
39      public void compute(ASTMethodDeclaration node) {
40  	//This includes Package Bodies and Object Type Bodies
41  	if (node.jjtGetParent() instanceof ASTPackageBody) {
42  	    this.computeNow(node);
43  	}
44      }
45  
46      public void compute(ASTProgramUnit node) {
47  	if (node.jjtGetParent() instanceof ASTPackageBody 
48              || node.jjtGetParent() instanceof ASTCompoundTriggerBlock // Treat Compound Trigger as a Package Body 
49             ) {
50  	    this.computeNow(node);
51  	}
52      }
53  
54      public void compute(ASTTypeMethod node) {
55  	if (node.jjtGetParent() instanceof ASTPackageBody) {
56  	    this.computeNow(node);
57  	}
58      }
59  
60      public void compute(ASTTriggerUnit node) {
61  	    this.computeNow(node);
62      }
63  
64      public void compute(ASTTriggerTimingPointSection node) {
65  	    this.computeNow(node);
66      }
67  
68      /* SRT public void compute(ASTConstructorDeclaration node) {
69  	this.computeNow(node);
70      }*/
71  
72      private void computeNow(Node node) {
73  	DataFlowNode inode = node.getDataFlowNode();
74  
75  	List<VariableAccess> undefinitions = markUsages(inode);
76  
77  	// all variables are first in state undefinition
78  	DataFlowNode firstINode = inode.getFlow().get(0);
79  	firstINode.setVariableAccess(undefinitions);
80  
81  	// all variables are getting undefined when leaving scope
82  	DataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
83  	lastINode.setVariableAccess(undefinitions);
84      }
85  
86      private List<VariableAccess> markUsages(DataFlowNode inode) {
87  	// undefinitions was once a field... seems like it works fine as a local
88  	List<VariableAccess> undefinitions = new ArrayList<>();
89  	Set<Map<NameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
90  	for (Map<NameDeclaration, List<NameOccurrence>> declarations : variableDeclarations) {
91  	    for (Map.Entry<NameDeclaration, List<NameOccurrence>> entry : declarations.entrySet()) {
92  		NameDeclaration vnd = entry.getKey();
93  
94  		if (vnd.getNode().jjtGetParent() instanceof ASTFormalParameter) {
95  		    // no definition/undefinition/references for parameters
96  		    continue;
97  		} else if (  vnd.getNode().jjtGetParent().getFirstDescendantOfType(ASTVariableOrConstantInitializer.class) != null) {
98  		    // add definition for initialized variables
99  		    addVariableAccess(vnd.getNode(), new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
100 			    inode.getFlow());
101 		}
102 		undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
103 
104 		for (NameOccurrence occurrence : entry.getValue()) {
105 		    addAccess(occurrence, inode);
106 		}
107 	    }
108 	}
109 	return undefinitions;
110     }
111 
112     private Set<Map<NameDeclaration, List<NameOccurrence>>> collectDeclarations(DataFlowNode inode) {
113 	Set<Map<NameDeclaration, List<NameOccurrence>>> decls = new HashSet<>();
114 	Map<NameDeclaration, List<NameOccurrence>> varDecls;
115 	for (int i = 0; i < inode.getFlow().size(); i++) {
116 	    DataFlowNode n = inode.getFlow().get(i);
117 	    if (n instanceof StartOrEndDataFlowNode) {
118 		continue;
119 	    }
120 	    varDecls = ((PLSQLNode)n.getNode()).getScope().getDeclarations();
121 	    if (!decls.contains(varDecls)) {
122 		decls.add(varDecls);
123 	    }
124 	}
125 	return decls;
126     }
127 
128     private void addAccess(NameOccurrence occ, DataFlowNode inode) {
129         PLSQLNameOccurrence occurrence = (PLSQLNameOccurrence)occ;
130 	if (occurrence.isOnLeftHandSide()) {
131 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence
132 		    .getImage()), inode.getFlow());
133 	} else if (occurrence.isOnRightHandSide() || !occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide()) {
134 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence
135 		    .getImage()), inode.getFlow());
136 	}
137     }
138 
139     /**
140      * Adds a VariableAccess to a dataflow node.
141      * @param node location of the access of a variable
142      * @param va variable access to add
143      * @param flow dataflownodes that can contain the node.
144      */
145     private void addVariableAccess(Node node, VariableAccess va, List<DataFlowNode> flow) {
146 	// backwards to find the right inode (not a method declaration)
147 	for (int i = flow.size() - 1; i > 0; i--) {
148 	    DataFlowNode inode = flow.get(i);
149 	    if (inode.getNode() == null) {
150 		continue;
151 	    }
152 
153 	    List<? extends Node> children = inode.getNode().findDescendantsOfType(node.getClass());
154 	    for (Node n : children) {
155 		if (node.equals(n)) {
156 		    List<VariableAccess> v = new ArrayList<>();
157 		    v.add(va);
158 		    inode.setVariableAccess(v);
159 		    return;
160 		}
161 	    }
162 	}
163     }
164 
165 }