DoubleCheckedLocking xref

View Javadoc
1 /**
2  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html 
3  */
4 package net.sourceforge.pmd.rules;
5 
6 import java.util.ArrayList;
7 import java.util.List;
8 
9 import net.sourceforge.pmd.AbstractJavaRule;
10 import net.sourceforge.pmd.ast.ASTAssignmentOperator;
11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
12 import net.sourceforge.pmd.ast.ASTCompilationUnit;
13 import net.sourceforge.pmd.ast.ASTFieldDeclaration;
14 import net.sourceforge.pmd.ast.ASTIfStatement;
15 import net.sourceforge.pmd.ast.ASTLiteral;
16 import net.sourceforge.pmd.ast.ASTMethodDeclaration;
17 import net.sourceforge.pmd.ast.ASTName;
18 import net.sourceforge.pmd.ast.ASTNullLiteral;
19 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
20 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
21 import net.sourceforge.pmd.ast.ASTReferenceType;
22 import net.sourceforge.pmd.ast.ASTReturnStatement;
23 import net.sourceforge.pmd.ast.ASTStatementExpression;
24 import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
25 import net.sourceforge.pmd.ast.ASTType;
26 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
27 import net.sourceforge.pmd.ast.Node;
28 
29 /**
30  * void method() {
31  * if(x == null) {
32  * synchronized(this){
33  * if(x == null) {
34  * x = new | method();
35  * }
36  * }
37  * }
38  * 1. The error is when one uses the value assigned within a synchronized
39  * section, outside of a synchronized section.
40  * if(x == null) is outside of synchronized section
41  * x = new | method();
42  * <p/>
43  * <p/>
44  * Very very specific check for double checked locking.
45  *
46  * @author CL Gilbert (dnoyeb@users.sourceforge.net)
47  */
48 public class DoubleCheckedLocking extends AbstractJavaRule {
49 
50 private List<String> volatileFields;
51 
52 @Override
53 public Object visit(ASTCompilationUnit compilationUnit,Object data) {
54 if ( this.volatileFields == null ) {
55 this.volatileFields = new ArrayList<String>(0);
56 } else {
57 this.volatileFields.clear();
58 }
59 return super.visit(compilationUnit,data);
60 }
61 
62 @Override
63 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
64 if (node.isInterface()) {
65 return data;
66 }
67 return super.visit(node, data);
68 }
69 
70 @Override
71 public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
72 if ( fieldDeclaration.isVolatile() ) {
73 	for (ASTVariableDeclaratorId declarator : fieldDeclaration.findChildrenOfType(ASTVariableDeclaratorId.class) ) {
74 this.volatileFields.add(declarator.getImage());
75 	}
76 }
77 return super.visit(fieldDeclaration, data);
78 }
79 
80 @Override
81 public Object visit(ASTMethodDeclaration node, Object data) {
82 if (node.getResultType().isVoid()) {
83 return super.visit(node, data);
84 }
85 
86 ASTType typeNode = (ASTType) node.getResultType().jjtGetChild(0);
87 if (typeNode.jjtGetNumChildren() == 0 || !(typeNode.jjtGetChild(0) instanceof ASTReferenceType)) {
88 return super.visit(node, data);
89 }
90 
91 List<ASTReturnStatement> rsl = new ArrayList<ASTReturnStatement>();
92 node.findChildrenOfType(ASTReturnStatement.class, rsl, true);
93 if (rsl.size() != 1) {
94 return super.visit(node, data);
95 }
96 ASTReturnStatement rs = rsl.get(0);
97 
98 List<ASTPrimaryExpression> pel = new ArrayList<ASTPrimaryExpression>();
99 rs.findChildrenOfType(ASTPrimaryExpression.class, pel, true);
100 ASTPrimaryExpression ape = pel.get(0);
101 Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1);
102 String returnVariableName = null;
103 if (lastChild instanceof ASTPrimaryPrefix) {
104 returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
105 }
106 // With Java5 and volatile keyword, DCL is no longer an issue
107 if (returnVariableName == null || this.volatileFields.contains(returnVariableName)) {
108 return super.visit(node, data);
109 }
110 List<ASTIfStatement> isl = new ArrayList<ASTIfStatement>();
111 node.findChildrenOfType(ASTIfStatement.class, isl, true);
112 if (isl.size() == 2) {
113 ASTIfStatement is = isl.get(0);
114 if (ifVerify(is, returnVariableName)) {
115 //find synchronized
116 List<ASTSynchronizedStatement> ssl = new ArrayList<ASTSynchronizedStatement>();
117 is.findChildrenOfType(ASTSynchronizedStatement.class, ssl, true);
118 if (ssl.size() == 1) {
119 ASTSynchronizedStatement ss = ssl.get(0);
120 isl.clear();
121 ss.findChildrenOfType(ASTIfStatement.class, isl, true);
122 if (isl.size() == 1) {
123 ASTIfStatement is2 = isl.get(0);
124 if (ifVerify(is2, returnVariableName)) {
125 List<ASTStatementExpression> sel = new ArrayList<ASTStatementExpression>();
126 is2.findChildrenOfType(ASTStatementExpression.class, sel, true);
127 if (sel.size() == 1) {
128 ASTStatementExpression se = sel.get(0);
129 if (se.jjtGetNumChildren() == 3) { //primaryExpression, AssignmentOperator, Expression
130 if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) {
131 ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0);
132 if (matchName(pe, returnVariableName)) {
133 if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
134 addViolation(data, node);
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142 }
143 }
144 }
145 return super.visit(node, data);
146 }
147 
148 private boolean ifVerify(ASTIfStatement is, String varname) {
149 List<ASTPrimaryExpression> finder = new ArrayList<ASTPrimaryExpression>();
150 is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
151 if (finder.size()> 1) { 
152 ASTPrimaryExpression nullStmt = findNonVariableStmt(varname,finder.get(0),finder.get(1));
153 if ( nullStmt != null ) {
154 if ((nullStmt.jjtGetNumChildren() == 1) && (nullStmt.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
155 ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) nullStmt.jjtGetChild(0);
156 if ((pp2.jjtGetNumChildren() == 1) && (pp2.jjtGetChild(0) instanceof ASTLiteral)) {
157 ASTLiteral lit = (ASTLiteral) pp2.jjtGetChild(0);
158 if ((lit.jjtGetNumChildren() == 1) && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) {
159 return true;
160 }
161 }
162 }
163 }
164 }
165 return false;
166 }
167 
168 /**
169  * <p>Sort out if apeLeft or apeRight are variable with the provided 'variableName'.</p>
170  * 
171  * @param variableName
172  * @param apeLeft
173  * @param apeRight
174  * @return reference from either apeLeft or apeRight, if one of them match, or 'null', if none match.
175  */
176 	private ASTPrimaryExpression findNonVariableStmt(String variableName,
177 			ASTPrimaryExpression apeLeft, ASTPrimaryExpression apeRight) {
178 	if (matchName(apeLeft, variableName) ) {
179 		return apeRight;
180 	}
181 	else if (matchName(apeRight, variableName) ) {
182 		return apeLeft;
183 	}
184 		return null;
185 	}
186 
187 	private boolean matchName(ASTPrimaryExpression ape, String name) {
188 if ((ape.jjtGetNumChildren() == 1) && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
189 ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0);
190 String name2 = getNameFromPrimaryPrefix(pp);
191 if (name2 != null && name2.equals(name)) {
192 return true;
193 }
194 }
195 return false;
196 }
197 
198 private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
199 if ((pp.jjtGetNumChildren() == 1) && (pp.jjtGetChild(0) instanceof ASTName)) {
200 return ((ASTName) pp.jjtGetChild(0)).getImage();
201 }
202 return null;
203 }
204 }

AltStyle によって変換されたページ (->オリジナル) /