001: package org.acm.seguin.pmd.rules;
002:
003: import org.acm.seguin.pmd.RuleContext;
004: import net.sourceforge.jrefactory.ast.ASTAssignmentOperator;
005: import net.sourceforge.jrefactory.ast.ASTClassDeclaration;
006: import net.sourceforge.jrefactory.ast.ASTIfStatement;
007: import net.sourceforge.jrefactory.ast.ASTInterfaceDeclaration;
008: import net.sourceforge.jrefactory.ast.ASTLiteral;
009: import net.sourceforge.jrefactory.ast.ASTMethodDeclaration;
010: import net.sourceforge.jrefactory.ast.ASTReferenceType;
011: import net.sourceforge.jrefactory.ast.ASTNestedClassDeclaration;
012: import net.sourceforge.jrefactory.ast.ASTNestedInterfaceDeclaration;
013: import net.sourceforge.jrefactory.ast.ASTNullLiteral;
014: import net.sourceforge.jrefactory.ast.ASTPrimaryExpression;
015: import net.sourceforge.jrefactory.ast.ASTPrimaryPrefix;
016: import net.sourceforge.jrefactory.ast.ASTResultType;
017: import net.sourceforge.jrefactory.ast.ASTReturnStatement;
018: import net.sourceforge.jrefactory.ast.ASTStatementExpression;
019: import net.sourceforge.jrefactory.ast.ASTSynchronizedStatement;
020: import net.sourceforge.jrefactory.ast.ASTName;
021: import net.sourceforge.jrefactory.ast.ASTType;
022: import net.sourceforge.jrefactory.ast.Node;
023:
024: import java.util.ArrayList;
025: import java.util.List;
026:
027: /**
028: * void method() {
029: * if(x == null) {
030: * synchronize(this){
031: * if(x == null) {
032: * x = new | method();
033: * }
034: * }
035: * }
036: * 1. The error is when one uses the value assigned within a synchronized
037: * section, outside of a synchronized section.
038: * if(x == null) is outside of synchronized section
039: * x = new | method();
040: *
041: *
042: * Very very specific check for double checked locking.
043: *
044: * @author CL Gilbert (dnoyeb@users.sourceforge.net)
045: */
046: public class DoubleCheckedLockingRule extends
047: org.acm.seguin.pmd.AbstractRule {
048:
049: private boolean interfaceSkipper;
050:
051: public Object visit(ASTMethodDeclaration node, Object data) {
052: if (interfaceSkipper == true) {//skip methods in interfaces
053: return super .visit(node, data);
054: }
055: int childNo = 0;
056: Node child = node.jjtGetFirstChild();
057: while (!(child instanceof ASTResultType)) {
058: child = node.jjtGetChild(++childNo);
059: }
060: ASTResultType rt = (ASTResultType) child;
061: if (rt.isVoid() == true) {
062: return super .visit(node, data);
063: }
064: ASTType t = (ASTType) rt.jjtGetFirstChild();
065: if (t.jjtGetNumChildren() == 0
066: || !(t.jjtGetFirstChild() instanceof ASTReferenceType)) {
067: return super .visit(node, data);
068: }
069: String returnVariableName = null;
070: List finder = new ArrayList();
071: GET_RETURN_VARIABLE_NAME: {
072: node.findChildrenOfType(ASTReturnStatement.class, finder,
073: true);
074: if (finder.size() != 1) {
075: return super .visit(node, data);
076: }
077: ASTReturnStatement rs = (ASTReturnStatement) finder.get(0);//EXPLODES IF THE CLASS IS AN INTERFACE SINCE NO RETURN
078:
079: finder.clear();
080: rs.findChildrenOfType(ASTPrimaryExpression.class, finder,
081: true);
082: ASTPrimaryExpression ape = (ASTPrimaryExpression) finder
083: .get(0);
084: Node lastChild = ape
085: .jjtGetChild(ape.jjtGetNumChildren() - 1);
086: if (lastChild instanceof ASTPrimaryPrefix) {
087: returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
088: }
089: if (returnVariableName == null) {
090: return super .visit(node, data);
091: }
092: }
093: CHECK_OUTER_IF: {
094: finder.clear();
095: node.findChildrenOfType(ASTIfStatement.class, finder, true);
096: if (finder.size() == 2) {
097: ASTIfStatement is = (ASTIfStatement) finder.get(0);
098: if (ifVerify(is, returnVariableName)) {
099: //find synchronize
100: finder.clear();
101: is.findChildrenOfType(
102: ASTSynchronizedStatement.class, finder,
103: true);
104: if (finder.size() == 1) {
105: ASTSynchronizedStatement ss = (ASTSynchronizedStatement) finder
106: .get(0);
107: finder.clear();
108: ss.findChildrenOfType(ASTIfStatement.class,
109: finder, true);
110: if (finder.size() == 1) {
111: ASTIfStatement is2 = (ASTIfStatement) finder
112: .get(0);
113: if (ifVerify(is2, returnVariableName)) {
114: finder.clear();
115: is2.findChildrenOfType(
116: ASTStatementExpression.class,
117: finder, true);
118: if (finder.size() == 1) {
119: ASTStatementExpression se = (ASTStatementExpression) finder
120: .get(0);
121: if (se.jjtGetNumChildren() == 3) { //primaryExpression, AssignmentOperator, Expression
122: if (se.jjtGetFirstChild() instanceof ASTPrimaryExpression) {
123: ASTPrimaryExpression pe = (ASTPrimaryExpression) se
124: .jjtGetFirstChild();
125: if (matchName(pe,
126: returnVariableName)) {
127: if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
128: RuleContext ctx = (RuleContext) data;
129: ctx
130: .getReport()
131: .addRuleViolation(
132: createRuleViolation(
133: ctx,
134: node
135: .getBeginLine()));
136: }
137: }
138: }
139: }
140: }
141: }
142: }
143: }
144: }
145: }
146: }
147: return super .visit(node, data);
148: }
149:
150: private boolean ifVerify(ASTIfStatement is, String varname) {
151: List finder = new ArrayList();
152: is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
153: if (finder.size() > 1) {
154: ASTPrimaryExpression apeLeft = (ASTPrimaryExpression) finder
155: .get(0);
156: if (matchName(apeLeft, varname)) {
157: ASTPrimaryExpression apeRight = (ASTPrimaryExpression) finder
158: .get(1);
159: if ((apeRight.jjtGetNumChildren() == 1)
160: && (apeRight.jjtGetFirstChild() instanceof ASTPrimaryPrefix)) {
161: ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight
162: .jjtGetFirstChild();
163: if ((pp2.jjtGetNumChildren() == 1)
164: && (pp2.jjtGetFirstChild() instanceof ASTLiteral)) {
165: ASTLiteral lit = (ASTLiteral) pp2
166: .jjtGetFirstChild();
167: if ((lit.jjtGetNumChildren() == 1)
168: && (lit.jjtGetFirstChild() instanceof ASTNullLiteral)) {
169: return true;
170: }
171: }
172: }
173: }
174: }
175: return false;
176: }
177:
178: public Object visit(ASTClassDeclaration node, Object data) {
179: boolean temp = interfaceSkipper;
180: interfaceSkipper = false;
181: // String className = ((ASTUnmodifiedClassDeclaration)node.jjtGetFirstChild()).getImage();
182: // System.out.println("classname = " + className);
183: Object o = super .visit(node, data);
184: interfaceSkipper = temp;
185: return o;
186: }
187:
188: public Object visit(ASTNestedClassDeclaration node, Object data) {
189: boolean temp = interfaceSkipper;
190: interfaceSkipper = false;
191: // String className = ((ASTUnmodifiedNestedClassDeclaration)node.jjtGetFirstChild()).getImage();
192: // System.out.println("classname = " + className);
193: Object o = super .visit(node, data);
194: interfaceSkipper = temp;
195: return o;
196: }
197:
198: public Object visit(ASTInterfaceDeclaration node, Object data) {
199: boolean temp = interfaceSkipper;
200: interfaceSkipper = true;
201: Object o = super .visit(node, data);
202: interfaceSkipper = temp;
203: return o;
204: }
205:
206: public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
207: boolean temp = interfaceSkipper;
208: interfaceSkipper = true;
209: Object o = super .visit(node, data);
210: interfaceSkipper = temp;
211: return o;
212: }
213:
214: public boolean matchName(ASTPrimaryExpression ape, String name) {
215: if ((ape.jjtGetNumChildren() == 1)
216: && (ape.jjtGetFirstChild() instanceof ASTPrimaryPrefix)) {
217: ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape
218: .jjtGetFirstChild();
219: String name2 = getNameFromPrimaryPrefix(pp);
220: if (name2 != null && name2.equals(name)) {
221: return true;
222: }
223: }
224: return false;
225: }
226:
227: public String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
228: if ((pp.jjtGetNumChildren() == 1)
229: && (pp.jjtGetFirstChild() instanceof ASTName)) {
230: String name2 = ((ASTName) pp.jjtGetFirstChild()).getImage();
231: return name2;
232: }
233: return null;
234: }
235: //Testing Section
236: // public Object visit(ASTCompilationUnit node, Object data) {
237: // interfaceSkipper = false; //safety
238: // try {
239: // return super.visit(node,data);
240: // }
241: // catch(Exception e){
242: // e.printStackTrace();
243: // throw new RuntimeException(e.getMessage());
244: // }
245: // }
246: // public Object visit(ASTMethodDeclarator node, Object data) {
247: // System.out.println("method = " + node.getImage());
248: // return super.visit(node,data);
249: // }
250: //
251: // public Object visit(ASTPackageDeclaration node, Object data){
252: // String name = ((ASTName)node.jjtGetFirstChild()).getImage();
253: // System.out.println("package = " + name);
254: // return super.visit(node, data);
255: // }
256: }
|