edu.buaa.satla.analysis.core.arg.ErrorPathShrinker.java Source code

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.core.arg.ErrorPathShrinker.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
*/

package edu.buaa.satla.analysis.core.arg;

import static com.google.common.collect.Iterables.indexOf;
import static edu.buaa.satla.analysis.util.AbstractStates.IS_TARGET_STATE;
import static edu.buaa.satla.analysis.util.VariableClassification.createFunctionReturnVariable;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.sosy_lab.cpachecker.cfa.ast.ABinaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.AFunctionCall;
import org.sosy_lab.cpachecker.cfa.ast.AFunctionCallAssignmentStatement;
import org.sosy_lab.cpachecker.cfa.ast.AFunctionCallExpression;
import org.sosy_lab.cpachecker.cfa.ast.AIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.ALiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.AParameterDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.AUnaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.AVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.IADeclaration;
import org.sosy_lab.cpachecker.cfa.ast.IAExpression;
import org.sosy_lab.cpachecker.cfa.ast.IAInitializer;
import org.sosy_lab.cpachecker.cfa.ast.IALeftHandSide;
import org.sosy_lab.cpachecker.cfa.ast.IARightHandSide;
import org.sosy_lab.cpachecker.cfa.ast.IASimpleDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.IAStatement;
import org.sosy_lab.cpachecker.cfa.ast.IAssignment;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFieldReference;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializerList;
import org.sosy_lab.cpachecker.cfa.ast.java.JBinaryExpression;
import org.sosy_lab.cpachecker.cfa.model.ADeclarationEdge;
import org.sosy_lab.cpachecker.cfa.model.AReturnStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.AStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.AssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.BlankEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.FunctionCallEdge;
import org.sosy_lab.cpachecker.cfa.model.FunctionEntryNode;
import org.sosy_lab.cpachecker.cfa.model.FunctionReturnEdge;
import org.sosy_lab.cpachecker.cfa.model.FunctionSummaryEdge;
import org.sosy_lab.cpachecker.cfa.model.MultiEdge;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

/** The Class ErrorPathShrinker gets an targetPath and creates a new Path,
 * with only the important edges of the Path. The idea behind this Class is,
 * that not every action (CFAEdge) before an error occurs is important for
 * the error, only a few actions (CFAEdges) are important.
 */
public final class ErrorPathShrinker {

    /** The short Path stores the result of PathHandler.handlePath(). */
    private Deque<CFAEdge> shortPath;

    /** This Set stores the important variables of the Path. */
    private Set<String> importantVars;

    /** This is the currently handled CFAEdge. */
    private CFAEdge currentCFAEdge;

    /** This is only an UtilityClass. */
    public ErrorPathShrinker() {
    }

    /** The function shrinkErrorPath gets an targetPath and creates a new Path,
     * with only the important edges of the Path.
     *
     * @param pTargetPath the "long" targetPath
     * @return errorPath the "short" errorPath */
    public List<CFAEdge> shrinkErrorPath(ARGPath pTargetPath) {
        List<CFAEdge> targetPath = getEdgesUntilTarget(pTargetPath);

        // create reverse iterator, from lastNode to firstNode
        final Iterator<CFAEdge> revIterator = Lists.reverse(targetPath).iterator();

        // Set for storing the important variables
        importantVars = new HashSet<>();

        // the short Path, the result
        final Deque<CFAEdge> shortErrorPath = new ArrayDeque<>();

        /* if the ErrorNode is inside of a function, the long path (pTargetPath) is not handled
         * until the StartNode, but only until the functionCall.
         * so update the sets of variables and call the PathHandler again until
         * the longPath is completely handled.*/
        while (revIterator.hasNext()) {
            handleEdge(revIterator.next(), shortErrorPath);
        }

        // TODO assertion disabled, until we can track all pointers completely
        // assert importantVars.isEmpty() : "some variables are never declared: " + importantVars;

        return ImmutableList.copyOf(shortErrorPath);
    }

    /** This method iterates a path and copies all the edges until
     * the target state into the result.
     *
     * @param path the Path to iterate */
    private static List<CFAEdge> getEdgesUntilTarget(final ARGPath path) {
        int targetPos = indexOf(path.asStatesList(), IS_TARGET_STATE);
        if (targetPos > 0) {
            return path.getInnerEdges().subList(0, targetPos);
        } else {
            return path.getInnerEdges();
        }
    }

    private void handleEdge(final CFAEdge cfaEdge, final Deque<CFAEdge> shortErrorPath) {
        currentCFAEdge = cfaEdge;
        shortPath = shortErrorPath;

        switch (cfaEdge.getEdgeType()) {

        case AssumeEdge:
            handleAssumption(((AssumeEdge) cfaEdge).getExpression());
            break;

        case FunctionCallEdge:
            final FunctionCallEdge fnkCall = (FunctionCallEdge) cfaEdge;
            final FunctionEntryNode succ = fnkCall.getSuccessor();
            handleFunctionCallEdge(fnkCall.getArguments(), succ.getFunctionParameters());
            break;

        case FunctionReturnEdge:
            final FunctionReturnEdge fnkReturnEdge = (FunctionReturnEdge) cfaEdge;
            final FunctionSummaryEdge summaryEdge = fnkReturnEdge.getSummaryEdge();
            handleFunctionReturnEdge(fnkReturnEdge, summaryEdge.getExpression());

            break;

        case MultiEdge:
            handleMultiEdge((MultiEdge) cfaEdge);
            break;

        default:
            handleSimpleEdge(cfaEdge);
        }
    }

    /** This function handles simple edges like Declarations, Statements,
     * ReturnStatements and BlankEdges.
     * They have in common, that they all can be part of an MultiEdge. */
    @SuppressWarnings("unchecked")
    private void handleSimpleEdge(final CFAEdge cfaEdge) {

        switch (cfaEdge.getEdgeType()) {
        case DeclarationEdge:
            handleDeclarationEdge(((ADeclarationEdge) cfaEdge).getDeclaration());
            break;

        case StatementEdge:
            handleStatementEdge(((AStatementEdge) cfaEdge).getStatement());
            break;

        case ReturnStatementEdge:
            // this statement is a function return, e.g. return (a);
            // note that this is different from return edge,
            // this is a statement edge, which leads the function to the
            // last node of its CFA, where return edge is from that last node
            // to the return site of the caller function
            handleReturnStatementEdge((AReturnStatementEdge) cfaEdge);
            break;

        case BlankEdge:
            handleBlankEdge((BlankEdge) cfaEdge);
            break;

        case CallToReturnEdge:
            throw new AssertionError("function summaries not supported");

        default:
            throw new AssertionError("unknown edge type");
        }
    }

    private void handleMultiEdge(MultiEdge cfaEdge) {
        for (final CFAEdge innerEdge : Lists.reverse(cfaEdge.getEdges())) {
            currentCFAEdge = innerEdge;
            handleSimpleEdge(innerEdge);
        }
        currentCFAEdge = cfaEdge; // reset edge
    }

    private void handleBlankEdge(BlankEdge cfaEdge) {
        assert cfaEdge == currentCFAEdge;
        if (currentCFAEdge.getSuccessor().isLoopStart()) {
            addCurrentCFAEdgeToShortPath();
        }
    }

    private void handleReturnStatementEdge(AReturnStatementEdge returnEdge) {
        final String retVal = createFunctionReturnVariable(returnEdge.getPredecessor().getFunctionName());
        if (isImportant(retVal)) {
            remove(retVal);
            if (returnEdge.getExpression().isPresent()) {
                addAllVarsInExpToSet(returnEdge.getExpression().get());
            }
        }
    }

    /** This method makes a recursive call of handlePath(). */
    private void handleFunctionReturnEdge(FunctionReturnEdge fnkReturnEdge, AFunctionCall expression) {

        if (expression instanceof AFunctionCallAssignmentStatement) {
            IALeftHandSide lhs = ((AFunctionCallAssignmentStatement) expression).getLeftHandSide();
            if (isImportant(lhs)) {
                remove(lhs);
                track(createFunctionReturnVariable(fnkReturnEdge.getPredecessor().getFunctionName()));
            }
        }
    }

    private void handleFunctionCallEdge(List<? extends IAExpression> arguments,
            List<? extends AParameterDeclaration> functionParameters) {
        addCurrentCFAEdgeToShortPath(); // functioncalls are important
        for (int i = 0; i < functionParameters.size(); i++) {
            IAExpression arg = arguments.get(i);
            final String paramName = functionParameters.get(i).getQualifiedName();
            if (isImportant(paramName)) {
                remove(paramName);
                addAllVarsInExpToSet(arg);
            }
        }
    }

    /** This method handles statements. */
    private void handleStatementEdge(IAStatement statementExp) {

        // expression is an assignment operation, e.g. a = b;
        if (statementExp instanceof IAssignment) {
            handleAssignmentToVariable(((IAssignment) statementExp).getLeftHandSide(),
                    ((IAssignment) statementExp).getRightHandSide());
        }

        // ext(); external functioncall
        else if (statementExp instanceof AFunctionCall) {
            addCurrentCFAEdgeToShortPath();
        }
    }

    /** This method handles the assignment of a variable (a = ?).
     *
     * @param lParam the local name of the variable to assign to
     * @param rightExp the assigning expression */
    private void handleAssignmentToVariable(final IALeftHandSide lParam, final IARightHandSide rightExp) {

        // FIRST add edge to the Path, THEN remove lParam from Set
        if (isImportant(lParam)) {
            addCurrentCFAEdgeToShortPath();

            // FIRST remove lParam, its history is unimportant.
            remove(lParam);

            // THEN update the Set
            addAllVarsInExpToSet(rightExp);
        }
    }

    /** This method handles variable declarations ("int a;" or "int b=a+123;"). */
    private void handleDeclarationEdge(IADeclaration declaration) {

        /* If the declared variable is important, the edge is important. */
        if (declaration.getName() != null) {
            if (isImportant(declaration)) {
                addCurrentCFAEdgeToShortPath();

                if (declaration instanceof AVariableDeclaration) {
                    IAInitializer init = ((AVariableDeclaration) declaration).getInitializer();
                    if (init != null) {
                        final Deque<IAInitializer> inits = new ArrayDeque<>();
                        inits.add(init);
                        while (!inits.isEmpty()) {
                            init = inits.pop();
                            if (init instanceof CInitializerExpression) {
                                addAllVarsInExpToSet(((CInitializerExpression) init).getExpression());
                            } else if (init instanceof CInitializerList) {
                                inits.addAll(((CInitializerList) init).getInitializers());
                            }
                        }
                    }
                }
                // the variable is declared in this statement,
                // so it is not important in the CFA before. --> remove it.
                remove(declaration);
            }
        }
    }

    /** This method handles assumptions (a==b, a<=b, true, etc.).
     * Assumptions are not handled as important edges, if they are part of a
     * switchStatement. Otherwise this method only adds all variables in an
     * assumption (expression) to the important variables. */
    private void handleAssumption(IAExpression assumeExp) {
        if (!isSwitchStatement(assumeExp)) {
            addAllVarsInExpToSet(assumeExp);
            addCurrentCFAEdgeToShortPath();
        }
    }

    /** This method checks, if the current assumption is part of a
     * switchStatement. Therefore it compares the current assumption with
     * the expression of the last added CFAEdge. It can also check similar
     * assumptions like  "if(x>3) {if(x>4){...}}".
     *
     * @param assumeExp the current assumption
     * @return is the assumption part of a switchStatement? */
    private boolean isSwitchStatement(final IAExpression assumeExp) {

        // Path can be empty at the end of a functionCall ("if (a) return b;")
        if (!shortPath.isEmpty()) {
            final CFAEdge lastEdge = shortPath.getFirst();

            //check, if the last edge was an assumption
            if (assumeExp instanceof ABinaryExpression && lastEdge instanceof AssumeEdge) {
                final AssumeEdge lastAss = (AssumeEdge) lastEdge;
                final IAExpression lastExp = lastAss.getExpression();

                // check, if the last edge was like "a==b"
                if (lastExp instanceof ABinaryExpression) {
                    final IAExpression currentBinExpOp1 = ((ABinaryExpression) assumeExp).getOperand1();
                    final IAExpression lastBinExpOp1 = ((ABinaryExpression) lastExp).getOperand1();

                    // only the first variable of the assignment is checked
                    final boolean isEqualVarName = currentBinExpOp1.toASTString()
                            .equals(lastBinExpOp1.toASTString());

                    // check, if lastEdge is the true-branch of "==" or the false-branch of "!="
                    ABinaryExpression aLastExp = ((ABinaryExpression) lastExp);
                    final boolean isEqualOp;

                    if (aLastExp instanceof CBinaryExpression) {
                        final CBinaryExpression.BinaryOperator op = (CBinaryExpression.BinaryOperator) aLastExp
                                .getOperator();
                        isEqualOp = (op == CBinaryExpression.BinaryOperator.EQUALS && lastAss.getTruthAssumption())
                                || (op == CBinaryExpression.BinaryOperator.NOT_EQUALS
                                        && !lastAss.getTruthAssumption());

                    } else {
                        final JBinaryExpression.BinaryOperator op = (JBinaryExpression.BinaryOperator) aLastExp
                                .getOperator();
                        isEqualOp = (op == JBinaryExpression.BinaryOperator.EQUALS && lastAss.getTruthAssumption())
                                || (op == JBinaryExpression.BinaryOperator.NOT_EQUALS
                                        && !lastAss.getTruthAssumption());

                    }

                    return (isEqualVarName && isEqualOp);
                }
            }
        }
        return false;
    }

    /** This method adds all variables in an expression to a Set.
     * If the expression exist of more than one sub-expressions,
     * the expression is divided into smaller parts and the method is called
     * recursively for each part, until there is only one variable or literal.
     * Literals are not part of important variables.
     *
     * @param exp the expression to be divided and added
     * @param importantVars all currently important variables
     */
    private void addAllVarsInExpToSet(final IARightHandSide exp) {

        // TODO replace with expression-visitor?

        // exp = 8.2 or "return;" (when exp == null),
        // this does not change the Set importantVars,
        if (exp instanceof ALiteralExpression || exp instanceof AFunctionCallExpression || exp == null) {
            // do nothing
        }

        // exp is an Identifier, i.e. the "b" from "a = b"
        else if (exp instanceof AIdExpression) {
            track((AIdExpression) exp);
        }

        // (cast) b
        else if (exp instanceof CCastExpression) {
            addAllVarsInExpToSet(((CCastExpression) exp).getOperand());
        }

        // -b
        else if (exp instanceof AUnaryExpression) {
            addAllVarsInExpToSet(((AUnaryExpression) exp).getOperand());
        }

        // b op c; --> b is operand1, c is operand2
        else if (exp instanceof ABinaryExpression) {
            final ABinaryExpression binExp = (ABinaryExpression) exp;
            addAllVarsInExpToSet(binExp.getOperand1());
            addAllVarsInExpToSet(binExp.getOperand2());
        }

        // a fieldReference "b->c" is handled as one variable with the name "b->c".
        else if (exp instanceof CFieldReference) {
            track((CFieldReference) exp);
        }
    }

    private boolean isImportant(IAExpression exp) {
        return isImportant(str(exp));
    }

    private boolean isImportant(IASimpleDeclaration exp) {
        return isImportant(exp.getQualifiedName());
    }

    private boolean isImportant(String var) {
        return importantVars.contains(var);
    }

    private void track(IAExpression exp) {
        track(str(exp));
    }

    private void track(String var) {
        importantVars.add(var);
    }

    private void remove(IAExpression exp) {
        remove(str(exp));
    }

    private void remove(IASimpleDeclaration exp) {
        remove(exp.getQualifiedName());
    }

    private void remove(String var) {
        importantVars.remove(var);
    }

    private String str(IAExpression exp) {
        if (exp instanceof AIdExpression) {
            return ((AIdExpression) exp).getDeclaration().getQualifiedName();
        } else {
            return exp.toASTString();
        }
    }

    /** This method adds the current CFAEdge in front of the shortPath. */
    private void addCurrentCFAEdgeToShortPath() {
        shortPath.addFirst(currentCFAEdge);
    }
}