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 net.sourceforge.pmd.ast.ASTAssignmentOperator;
    7   import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
    8   import net.sourceforge.pmd.ast.ASTIfStatement;
    9   import net.sourceforge.pmd.ast.ASTLiteral;
    10  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    11  import net.sourceforge.pmd.ast.ASTName;
    12  import net.sourceforge.pmd.ast.ASTNullLiteral;
    13  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
    14  import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
    15  import net.sourceforge.pmd.ast.ASTReferenceType;
    16  import net.sourceforge.pmd.ast.ASTReturnStatement;
    17  import net.sourceforge.pmd.ast.ASTStatementExpression;
    18  import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
    19  import net.sourceforge.pmd.ast.ASTType;
    20  import net.sourceforge.pmd.ast.Node;
    21
    22  import java.util.ArrayList;
    23  import java.util.List;
    24
    25  /**
    26   * void method() {
    27   * if(x == null) {
    28   * synchronized(this){
    29   * if(x == null) {
    30   * x = new | method();
    31   * }
    32   * }
    33   * }
    34   * 1.  The error is when one uses the value assigned within a synchronized
    35   * section, outside of a synchronized section.
    36   * if(x == null) is outside of synchronized section
    37   * x = new | method();
    38   * <p/>
    39   * <p/>
    40   * Very very specific check for double checked locking.
    41   *
    42   * @author CL Gilbert (dnoyeb@users.sourceforge.net)
    43   */
    44  public class DoubleCheckedLocking extends net.sourceforge.pmd.AbstractRule {
    45
    46      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    47          if (node.isInterface()) {
    48              return data;
    49          }
    50          return super.visit(node, data);
    51      }
    52
    53      public Object visit(ASTMethodDeclaration node, Object data) {
    54          if (node.getResultType().isVoid()) {
    55              return super.visit(node, data);
    56          }
    57
    58          ASTType typeNode = (ASTType) node.getResultType().jjtGetChild(0);
    59          if (typeNode.jjtGetNumChildren() == 0 || !(typeNode.jjtGetChild(0) instanceof ASTReferenceType)) {
    60              return super.visit(node, data);
    61          }
    62
    63          List<ASTReturnStatement> rsl = new ArrayList<ASTReturnStatement>();
    64          node.findChildrenOfType(ASTReturnStatement.class, rsl, true);
    65          if (rsl.size() != 1) {
    66              return super.visit(node, data);
    67          }
    68          ASTReturnStatement rs = rsl.get(0);
    69
    70          List<ASTPrimaryExpression> pel = new ArrayList<ASTPrimaryExpression>();
    71          rs.findChildrenOfType(ASTPrimaryExpression.class, pel, true);
    72          ASTPrimaryExpression ape = pel.get(0);
    73          Node lastChild = ape.jjtGetChild(ape.jjtGetNumChildren() - 1);
    74          String returnVariableName = null;
    75          if (lastChild instanceof ASTPrimaryPrefix) {
    76              returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
    77          }
    78          if (returnVariableName == null) {
    79              return super.visit(node, data);
    80          }
    81          List<ASTIfStatement> isl = new ArrayList<ASTIfStatement>();
    82          node.findChildrenOfType(ASTIfStatement.class, isl, true);
    83          if (isl.size() == 2) {
    84              ASTIfStatement is = isl.get(0);
    85              if (ifVerify(is, returnVariableName)) {
    86                  //find synchronized
    87                  List<ASTSynchronizedStatement> ssl = new ArrayList<ASTSynchronizedStatement>();
    88                  is.findChildrenOfType(ASTSynchronizedStatement.class, ssl, true);
    89                  if (ssl.size() == 1) {
    90                      ASTSynchronizedStatement ss = ssl.get(0);
    91                      isl.clear();
    92                      ss.findChildrenOfType(ASTIfStatement.class, isl, true);
    93                      if (isl.size() == 1) {
    94                          ASTIfStatement is2 = isl.get(0);
    95                          if (ifVerify(is2, returnVariableName)) {
    96                              List<ASTStatementExpression> sel = new ArrayList<ASTStatementExpression>();
    97                              is2.findChildrenOfType(ASTStatementExpression.class, sel, true);
    98                              if (sel.size() == 1) {
    99                                  ASTStatementExpression se = sel.get(0);
    100                                 if (se.jjtGetNumChildren() == 3) { //primaryExpression, AssignmentOperator, Expression
    101                                     if (se.jjtGetChild(0) instanceof ASTPrimaryExpression) {
    102                                         ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0);
    103                                         if (matchName(pe, returnVariableName)) {
    104                                             if (se.jjtGetChild(1) instanceof ASTAssignmentOperator) {
    105                                                 addViolation(data, node);
    106                                             }
    107                                         }
    108                                     }
    109                                 }
    110                             }
    111                         }
    112                     }
    113                 }
    114             }
    115         }
    116         return super.visit(node, data);
    117     }
    118
    119     private boolean ifVerify(ASTIfStatement is, String varname) {
    120         List<ASTPrimaryExpression> finder = new ArrayList<ASTPrimaryExpression>();
    121         is.findChildrenOfType(ASTPrimaryExpression.class, finder, true);
    122         if (finder.size() > 1) {
    123             ASTPrimaryExpression apeLeft = finder.get(0);
    124             if (matchName(apeLeft, varname)) {
    125                 ASTPrimaryExpression apeRight = finder.get(1);
    126                 if ((apeRight.jjtGetNumChildren() == 1) && (apeRight.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
    127                     ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) apeRight.jjtGetChild(0);
    128                     if ((pp2.jjtGetNumChildren() == 1) && (pp2.jjtGetChild(0) instanceof ASTLiteral)) {
    129                         ASTLiteral lit = (ASTLiteral) pp2.jjtGetChild(0);
    130                         if ((lit.jjtGetNumChildren() == 1) && (lit.jjtGetChild(0) instanceof ASTNullLiteral)) {
    131                             return true;
    132                         }
    133                     }
    134                 }
    135             }
    136         }
    137         return false;
    138     }
    139
    140     private boolean matchName(ASTPrimaryExpression ape, String name) {
    141         if ((ape.jjtGetNumChildren() == 1) && (ape.jjtGetChild(0) instanceof ASTPrimaryPrefix)) {
    142             ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.jjtGetChild(0);
    143             String name2 = getNameFromPrimaryPrefix(pp);
    144             if (name2 != null && name2.equals(name)) {
    145                 return true;
    146             }
    147         }
    148         return false;
    149     }
    150
    151     private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) {
    152         if ((pp.jjtGetNumChildren() == 1) && (pp.jjtGetChild(0) instanceof ASTName)) {
    153             return ((ASTName) pp.jjtGetChild(0)).getImage();
    154         }
    155         return null;
    156     }
    157 }