001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules;
004:
005: import net.sourceforge.pmd.ast.ASTAssignmentOperator;
006: import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
007: import net.sourceforge.pmd.ast.ASTIfStatement;
008: import net.sourceforge.pmd.ast.ASTLiteral;
009: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
010: import net.sourceforge.pmd.ast.ASTName;
011: import net.sourceforge.pmd.ast.ASTNullLiteral;
012: import net.sourceforge.pmd.ast.ASTPrimaryExpression;
013: import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
014: import net.sourceforge.pmd.ast.ASTReferenceType;
015: import net.sourceforge.pmd.ast.ASTReturnStatement;
016: import net.sourceforge.pmd.ast.ASTStatementExpression;
017: import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
018: import net.sourceforge.pmd.ast.ASTType;
019: import net.sourceforge.pmd.ast.Node;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: /**
025: * void method() {
026: * if(x == null) {
027: * synchronized(this){
028: * if(x == null) {
029: * x = new | method();
030: * }
031: * }
032: * }
033: * 1. The error is when one uses the value assigned within a synchronized
034: * section, outside of a synchronized section.
035: * if(x == null) is outside of synchronized section
036: * x = new | method();
037: * <p/>
038: * <p/>
039: * Very very specific check for double checked locking.
040: *
041: * @author CL Gilbert (dnoyeb@users.sourceforge.net)
042: */
043: public class DoubleCheckedLocking extends
044: net.sourceforge.pmd.AbstractRule {
045:
046: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
047: if (node.isInterface()) {
048: return data;
049: }
050: return super .visit(node, data);
051: }
052:
053: public Object visit(ASTMethodDeclaration node, Object data) {
054: if (node.getResultType().isVoid()) {
055: return super .visit(node, data);
056: }
057:
058: ASTType typeNode = (ASTType) node.getResultType()
059: .jjtGetChild(0);
060: if (typeNode.jjtGetNumChildren() == 0
061: || !(typeNode.jjtGetChild(0) instanceof ASTReferenceType)) {
062: return super .visit(node, data);
063: }
064:
065: List<ASTReturnStatement> rsl = new ArrayList<ASTReturnStatement>();
066: node.findChildrenOfType(ASTReturnStatement.class, rsl, true);
067: if (rsl.size() != 1) {
068: return super .visit(node, data);
069: }
070: ASTReturnStatement rs = rsl.get(0);
071:
072: List<ASTPrimaryExpression> pel = new ArrayList<ASTPrimaryExpression>();
073: rs.findChildrenOfType(ASTPrimaryExpression.class, pel, true);
074: ASTPrimaryExpression ape = pel.get(0);
075: Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1);
076: String returnVariableName = null;
077: if (lastChild instanceof ASTPrimaryPrefix) {
078: returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
079: }
080: if (returnVariableName == null) {
081: return super .visit(node, data);
082: }
083: List<ASTIfStatement> isl = new ArrayList<ASTIfStatement>();
084: node.findChildrenOfType(ASTIfStatement.class, isl, true);
085: if (isl.size() == 2) {
086: ASTIfStatement is = isl.get(0);
087: if (ifVerify(is, returnVariableName)) {
088: //find synchronized
089: List<ASTSynchronizedStatement> ssl = new ArrayList<ASTSynchronizedStatement>();
090: is.findChildrenOfType(ASTSynchronizedStatement.class,
091: ssl, true);
092: if (ssl.size() == 1) {
093: ASTSynchronizedStatement ss = ssl.get(0);
094: isl.clear();
095: ss.findChildrenOfType(ASTIfStatement.class, isl,
096: true);
097: if (isl.size() == 1) {
098: ASTIfStatement is2 = isl.get(0);
099: if (ifVerify(is2, returnVariableName)) {
100: List<ASTStatementExpression> sel = new ArrayList<ASTStatementExpression>();
101: is2.findChildrenOfType(
102: ASTStatementExpression.class, sel,
103: true);
104: if (sel.size() == 1) {
105: ASTStatementExpression se = sel.get(0);
106: if (se.jjtGetNumChildren() == 3) { //primaryExpression, AssignmentOperator, Expression
107: if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) {
108: ASTPrimaryExpression pe = (ASTPrimaryExpression) se
109: .jjtGetChild(0);
110: if (matchName(pe,
111: returnVariableName)) {
112: if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
113: addViolation(data, node);
114: }
115: }
116: }
117: }
118: }
119: }
120: }
121: }
122: }
123: }
124: return super .visit(node, data);
125: }
126:
127: private boolean ifVerify(ASTIfStatement is, String varname) {
128: List<ASTPrimaryExpression> finder = new ArrayList<ASTPrimaryExpression>();
129: is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
130: if (finder.size() > 1) {
131: ASTPrimaryExpression apeLeft = finder.get(0);
132: if (matchName(apeLeft, varname)) {
133: ASTPrimaryExpression apeRight = finder.get(1);
134: if ((apeRight.jjtGetNumChildren() == 1)
135: && (apeRight.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
136: ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight
137: .jjtGetChild(0);
138: if ((pp2.jjtGetNumChildren() == 1)
139: && (pp2.jjtGetChild(0) instanceof ASTLiteral)) {
140: ASTLiteral lit = (ASTLiteral) pp2
141: .jjtGetChild(0);
142: if ((lit.jjtGetNumChildren() == 1)
143: && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) {
144: return true;
145: }
146: }
147: }
148: }
149: }
150: return false;
151: }
152:
153: private boolean matchName(ASTPrimaryExpression ape, String name) {
154: if ((ape.jjtGetNumChildren() == 1)
155: && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
156: ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0);
157: String name2 = getNameFromPrimaryPrefix(pp);
158: if (name2 != null && name2.equals(name)) {
159: return true;
160: }
161: }
162: return false;
163: }
164:
165: private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
166: if ((pp.jjtGetNumChildren() == 1)
167: && (pp.jjtGetChild(0) instanceof ASTName)) {
168: return ((ASTName) pp.jjtGetChild(0)).getImage();
169: }
170: return null;
171: }
172: }
|