/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.rules; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.ast.ASTAssignmentOperator; import net.sourceforge.pmd.ast.ASTClassDeclaration; import net.sourceforge.pmd.ast.ASTIfStatement; import net.sourceforge.pmd.ast.ASTInterfaceDeclaration; import net.sourceforge.pmd.ast.ASTLiteral; import net.sourceforge.pmd.ast.ASTMethodDeclaration; import net.sourceforge.pmd.ast.ASTName; import net.sourceforge.pmd.ast.ASTNestedClassDeclaration; import net.sourceforge.pmd.ast.ASTNestedInterfaceDeclaration; import net.sourceforge.pmd.ast.ASTNullLiteral; import net.sourceforge.pmd.ast.ASTPrimaryExpression; import net.sourceforge.pmd.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.ast.ASTResultType; import net.sourceforge.pmd.ast.ASTReturnStatement; import net.sourceforge.pmd.ast.ASTStatementExpression; import net.sourceforge.pmd.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.ast.ASTType; import net.sourceforge.pmd.ast.Node; import java.util.ArrayList; import java.util.List; /** * void method() { * if(x == null) { * synchronized(this){ * if(x == null) { * x = new | method(); * } * } * } * 1. The error is when one uses the value assigned within a synchronized * section, outside of a synchronized section. * if(x == null) is outside of synchronized section * x = new | method(); * * * Very very specific check for double checked locking. * * @author CL Gilbert (dnoyeb@users.sourceforge.net) */ public class DoubleCheckedLockingRule extends net.sourceforge.pmd.AbstractRule { private boolean interfaceSkipper; public Object visit(ASTMethodDeclaration node, Object data) { if (interfaceSkipper == true) {//skip methods in interfaces return super.visit(node, data); } ASTResultType rt = (ASTResultType) node.jjtGetChild(0); if (rt.isVoid() == true) { return super.visit(node, data); } ASTType t = (ASTType) rt.jjtGetChild(0); if (t.jjtGetNumChildren() == 0 || !(t.jjtGetChild(0) instanceof ASTName)) { return super.visit(node, data); } String returnVariableName = null; List finder = new ArrayList(); GET_RETURN_VARIABLE_NAME:{ node.findChildrenOfType(ASTReturnStatement.class, finder, true); if (finder.size() != 1) { return super.visit(node, data); } ASTReturnStatement rs = (ASTReturnStatement) finder.get(0);//EXPLODES IF THE CLASS IS AN INTERFACE SINCE NO RETURN finder.clear(); rs.findChildrenOfType(ASTPrimaryExpression.class, finder, true); ASTPrimaryExpression ape = (ASTPrimaryExpression) finder.get(0); Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1); if (lastChild instanceof ASTPrimaryPrefix) { returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild); } if (returnVariableName == null) { return super.visit(node, data); } } CHECK_OUTER_IF:{ finder.clear(); node.findChildrenOfType(ASTIfStatement.class, finder, true); if (finder.size() == 2) { ASTIfStatement is = (ASTIfStatement) finder.get(0); if (ifVerify(is, returnVariableName)) { //find synchronized finder.clear(); is.findChildrenOfType(ASTSynchronizedStatement.class, finder, true); if (finder.size() == 1) { ASTSynchronizedStatement ss = (ASTSynchronizedStatement) finder.get(0); finder.clear(); ss.findChildrenOfType(ASTIfStatement.class, finder, true); if (finder.size() == 1) { ASTIfStatement is2 = (ASTIfStatement) finder.get(0); if (ifVerify(is2, returnVariableName)) { finder.clear(); is2.findChildrenOfType(ASTStatementExpression.class, finder, true); if (finder.size() == 1) { ASTStatementExpression se = (ASTStatementExpression) finder.get(0); if (se.jjtGetNumChildren() == 3) { //primaryExpression, AssignmentOperator, Expression if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) { ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0); if (matchName(pe, returnVariableName)) { if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) { RuleContext ctx = (RuleContext) data; ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine())); } } } } } } } } } } } return super.visit(node, data); } private boolean ifVerify(ASTIfStatement is, String varname) { List finder = new ArrayList(); is.findChildrenOfType(ASTPrimaryExpression.class, finder, true); if (finder.size() > 1) { ASTPrimaryExpression apeLeft = (ASTPrimaryExpression) finder.get(0); if (matchName(apeLeft, varname)) { ASTPrimaryExpression apeRight = (ASTPrimaryExpression) finder.get(1); if ((apeRight.jjtGetNumChildren() == 1) && (apeRight.jjtGetChild(0) instanceof ASTPrimaryPrefix)) { ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight.jjtGetChild(0); if ((pp2.jjtGetNumChildren() == 1) && (pp2.jjtGetChild(0) instanceof ASTLiteral)) { ASTLiteral lit = (ASTLiteral) pp2.jjtGetChild(0); if ((lit.jjtGetNumChildren() == 1) && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) { return true; } } } } } return false; } public Object visit(ASTClassDeclaration node, Object data) { boolean temp = interfaceSkipper; interfaceSkipper = false; // String className = ((ASTUnmodifiedClassDeclaration)node.jjtGetChild(0)).getImage(); // System.out.println("classname = " + className); Object o = super.visit(node, data); interfaceSkipper = temp; return o; } public Object visit(ASTNestedClassDeclaration node, Object data) { boolean temp = interfaceSkipper; interfaceSkipper = false; // String className = ((ASTUnmodifiedNestedClassDeclaration)node.jjtGetChild(0)).getImage(); // System.out.println("classname = " + className); Object o = super.visit(node, data); interfaceSkipper = temp; return o; } public Object visit(ASTInterfaceDeclaration node, Object data) { boolean temp = interfaceSkipper; interfaceSkipper = true; Object o = super.visit(node, data); interfaceSkipper = temp; return o; } public Object visit(ASTNestedInterfaceDeclaration node, Object data) { boolean temp = interfaceSkipper; interfaceSkipper = true; Object o = super.visit(node, data); interfaceSkipper = temp; return o; } public boolean matchName(ASTPrimaryExpression ape, String name) { if ((ape.jjtGetNumChildren() == 1) && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) { ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0); String name2 = getNameFromPrimaryPrefix(pp); if (name2 != null && name2.equals(name)) { return true; } } return false; } public String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) { if ((pp.jjtGetNumChildren() == 1) && (pp.jjtGetChild(0) instanceof ASTName)) { String name2 = ((ASTName) pp.jjtGetChild(0)).getImage(); return name2; } return null; } //Testing Section // public Object visit(ASTCompilationUnit node, Object data) { // interfaceSkipper = false; //safety // try { // return super.visit(node,data); // } // catch(Exception e){ // e.printStackTrace(); // throw new RuntimeException(e.getMessage()); // } // } // public Object visit(ASTMethodDeclarator node, Object data) { // System.out.println("method = " + node.getImage()); // return super.visit(node,data); // } // // public Object visit(ASTPackageDeclaration node, Object data){ // String name = ((ASTName)node.jjtGetChild(0)).getImage(); // System.out.println("package = " + name); // return super.visit(node, data); // } }