/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.dfa; import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.pmd.lang.DataFlowHandler; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.dfa.Linker; import net.sourceforge.pmd.lang.dfa.LinkerException; import net.sourceforge.pmd.lang.dfa.NodeType; import net.sourceforge.pmd.lang.dfa.SequenceException; import net.sourceforge.pmd.lang.dfa.Structure; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTContinueStatement; import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTForInit; import net.sourceforge.pmd.lang.java.ast.ASTForStatement; import net.sourceforge.pmd.lang.java.ast.ASTForUpdate; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTLabeledStatement; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel; import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter; /** * Sublayer of DataFlowFacade. Finds all data flow nodes and stores the * type information (@see StackObject). At last it uses this information * to link the nodes. * * @author raik */ public class StatementAndBraceFinder extends JavaParserVisitorAdapter { private static final Logger LOGGER = Logger.getLogger(StatementAndBraceFinder.class.getName()); private final DataFlowHandler dataFlowHandler; private Structure dataFlow; public StatementAndBraceFinder(DataFlowHandler dataFlowHandler) { this.dataFlowHandler = dataFlowHandler; } public void buildDataFlowFor(JavaNode node) { if (!(node instanceof ASTMethodDeclaration) && !(node instanceof ASTConstructorDeclaration)) { throw new RuntimeException("Can't build a data flow for anything other than a method or a constructor"); } this.dataFlow = new Structure(dataFlowHandler); this.dataFlow.createStartNode(node.getBeginLine()); this.dataFlow.createNewNode(node); node.jjtAccept(this, dataFlow); this.dataFlow.createEndNode(node.getEndLine()); if (LOGGER.isLoggable(Level.FINE)) { // TODO SRT Remove after development LOGGER.fine("DataFlow is " + this.dataFlow.dump()); } Linker linker = new Linker(dataFlowHandler, dataFlow.getBraceStack(), dataFlow.getContinueBreakReturnStack()); try { linker.computePaths(); } catch (LinkerException e) { e.printStackTrace(); } catch (SequenceException e) { e.printStackTrace(); } } @Override public Object visit(ASTStatementExpression node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("createNewNode ASTStatementExpression: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } dataFlow.createNewNode(node); return super.visit(node, data); } @Override public Object visit(ASTVariableDeclarator node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("createNewNode ASTVariableDeclarator: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } dataFlow.createNewNode(node); return super.visit(node, data); } @Override public Object visit(ASTExpression node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; // TODO what about throw stmts? if (node.jjtGetParent() instanceof ASTIfStatement) { dataFlow.createNewNode(node); // START IF dataFlow.pushOnStack(NodeType.IF_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack parent IF_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTWhileStatement) { dataFlow.createNewNode(node); // START WHILE dataFlow.pushOnStack(NodeType.WHILE_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack parent WHILE_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTSwitchStatement) { dataFlow.createNewNode(node); // START SWITCH dataFlow.pushOnStack(NodeType.SWITCH_START, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack parent SWITCH_START: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTForStatement) { dataFlow.createNewNode(node); // FOR EXPR dataFlow.pushOnStack(NodeType.FOR_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack parent FOR_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTDoStatement) { dataFlow.createNewNode(node); // DO EXPR dataFlow.pushOnStack(NodeType.DO_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack parent DO_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } return super.visit(node, data); } @Override public Object visit(ASTForInit node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; super.visit(node, data); dataFlow.pushOnStack(NodeType.FOR_INIT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_INIT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } this.addForExpressionNode(node, dataFlow); return data; } @Override public Object visit(ASTLabeledStatement node, Object data) { dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.LABEL_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest( "pushOnStack LABEL_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return super.visit(node, data); } @Override public Object visit(ASTForUpdate node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; this.addForExpressionNode(node, dataFlow); super.visit(node, data); dataFlow.pushOnStack(NodeType.FOR_UPDATE, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_UPDATE: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return data; } // ---------------------------------------------------------------------------- // BRANCH OUT @Override public Object visit(ASTStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; if (node.jjtGetParent() instanceof ASTForStatement) { this.addForExpressionNode(node, dataFlow); dataFlow.pushOnStack(NodeType.FOR_BEFORE_FIRST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_BEFORE_FIRST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTDoStatement) { dataFlow.pushOnStack(NodeType.DO_BEFORE_FIRST_STATEMENT, dataFlow.getLast()); dataFlow.createNewNode(node.jjtGetParent()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack DO_BEFORE_FIRST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } super.visit(node, data); if (node.jjtGetParent() instanceof ASTIfStatement) { ASTIfStatement st = (ASTIfStatement) node.jjtGetParent(); if (!st.hasElse()) { dataFlow.pushOnStack(NodeType.IF_LAST_STATEMENT_WITHOUT_ELSE, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack IF_LAST_STATEMENT_WITHOUT_ELSE: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (st.hasElse() && !st.jjtGetChild(1).equals(node)) { dataFlow.pushOnStack(NodeType.ELSE_LAST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack ELSE_LAST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else { dataFlow.pushOnStack(NodeType.IF_LAST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack IF_LAST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } } else if (node.jjtGetParent() instanceof ASTWhileStatement) { dataFlow.pushOnStack(NodeType.WHILE_LAST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack WHILE_LAST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTForStatement) { dataFlow.pushOnStack(NodeType.FOR_END, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_END: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node.jjtGetParent() instanceof ASTLabeledStatement) { dataFlow.pushOnStack(NodeType.LABEL_LAST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack LABEL_LAST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } return data; } @Override public Object visit(ASTSwitchStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; super.visit(node, data); dataFlow.pushOnStack(NodeType.SWITCH_END, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack SWITCH_END: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return data; } @Override public Object visit(ASTSwitchLabel node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; // super.visit(node, data); if (node.jjtGetNumChildren() == 0) { dataFlow.pushOnStack(NodeType.SWITCH_LAST_DEFAULT_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack SWITCH_LAST_DEFAULT_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else { dataFlow.pushOnStack(NodeType.CASE_LAST_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack CASE_LAST_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } return data; } @Override public Object visit(ASTBreakStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.BREAK_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest( "pushOnStack BREAK_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return super.visit(node, data); } @Override public Object visit(ASTContinueStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.CONTINUE_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack CONTINUE_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return super.visit(node, data); } @Override public Object visit(ASTReturnStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.RETURN_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest( "pushOnStack RETURN_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return super.visit(node, data); } @Override public Object visit(ASTThrowStatement node, Object data) { if (!(data instanceof Structure)) { return data; } Structure dataFlow = (Structure) data; dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.THROW_STATEMENT, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest( "pushOnStack THROW_STATEMENT: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } return super.visit(node, data); } /* * The method handles the special "for" loop. It creates always an * expression node even if the loop looks like for(;;). */ private void addForExpressionNode(Node node, Structure dataFlow) { ASTForStatement parent = (ASTForStatement) node.jjtGetParent(); boolean hasExpressionChild = false; boolean hasForInitNode = false; boolean hasForUpdateNode = false; for (int i = 0; i < parent.jjtGetNumChildren(); i++) { if (parent.jjtGetChild(i) instanceof ASTExpression) { hasExpressionChild = true; } else if (parent.jjtGetChild(i) instanceof ASTForUpdate) { hasForUpdateNode = true; } else if (parent.jjtGetChild(i) instanceof ASTForInit) { hasForInitNode = true; } } if (!hasExpressionChild) { if (node instanceof ASTForInit) { dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.FOR_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest( "pushOnStack FOR_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } else if (node instanceof ASTForUpdate) { if (!hasForInitNode) { dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.FOR_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } } else if (node instanceof ASTStatement) { if (!hasForInitNode && !hasForUpdateNode) { dataFlow.createNewNode(node); dataFlow.pushOnStack(NodeType.FOR_EXPR, dataFlow.getLast()); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.finest("pushOnStack FOR_EXPR: line " + node.getBeginLine() + ", column " + node.getBeginColumn()); } } } } } }