Package edu.cmu.cs.crystal.flow

Source Code of edu.cmu.cs.crystal.flow.MotherFlowAnalysis

/**
* Copyright (c) 2006, 2007, 2008 Marwan Abi-Antoun, Jonathan Aldrich, Nels E. Beckman,
* Kevin Bierhoff, David Dickey, Ciera Jaspan, Thomas LaToza, Gabriel Zenarosa, and others.
*
* This file is part of Crystal.
*
* Crystal is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Crystal is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Crystal.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.cmu.cs.crystal.flow;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.MethodDeclaration;

import edu.cmu.cs.crystal.cfg.ICFGNode;
import edu.cmu.cs.crystal.flow.worklist.AnalysisResult;
import edu.cmu.cs.crystal.flow.worklist.WorklistFactory;
import edu.cmu.cs.crystal.flow.worklist.WorklistTemplate;
import edu.cmu.cs.crystal.util.Option;
import edu.cmu.cs.crystal.util.Utilities;

/**
* Abstract base class for flow analyses that implements a worklist algorithm
* and provides various methods to access analysis results.  Methods are analyzed
* lazily when results for AST nodes inside a method are requested.
*
* @author David Dickey
* @author Jonathan Aldrich
* @author Kevin Bierhoff
* @author Ciera Jaspan
*
* @param <LE> the type that represents the analysis knowledge
*/
public abstract class MotherFlowAnalysis<LE> implements IFlowAnalysis<LE> {
 
  public static final Logger log = Logger.getLogger(MotherFlowAnalysis.class.getName());
 
  /**
   * the resulting lattices for each node in the control flow graph
   * for the method analyzed last.  Whenever results for an AST node
   * are requested that are not in the last analyzed method, the current
   * results are tossed and replaced with results for the method surrounding
   * that node.
   */  
  protected Map<ICFGNode<ASTNode>, IResult<LE>> labeledResultsBefore = new HashMap<ICFGNode<ASTNode>, IResult<LE>>();
  protected Map<ICFGNode<ASTNode>, IResult<LE>> labeledResultsAfter = new HashMap<ICFGNode<ASTNode>, IResult<LE>>();
 
  /**
   * Information about the method that was analyzed last. 
   */
  private MethodDeclaration currentMethod;
  private ILatticeOperations<LE> currentLattice;
 
  /**
   * Map to find CFGNodes corresponding to AST nodes.
   */
  private Map<ASTNode, Set<ICFGNode<ASTNode>>> nodeMap = new HashMap<ASTNode, Set<ICFGNode<ASTNode>>>();

  private final WorklistFactory factory;

  private ICFGNode<ASTNode> cfgStartNode;

  private ICFGNode<ASTNode> cfgEndNode;
 
  /**
   * Initializes a fresh flow analysis object.
   */
  public MotherFlowAnalysis() {
    this.factory = new WorklistFactory();
  }
 
  /**
   * Use the given progress monitor to cancel subsequent flow analysis runs.
   * Previously computed results may still be available.
   * <i>If</i> a monitor is set then subsequent accesses to analysis results
   * may through a {@link java.util.concurrent.CancellationException} which,
   * if not caught, will abort the current overall Crystal analysis job.
   * @param monitor Monitor to listen for cancellation or {@link Option#none()}
   * if worklist runs should not be canceled.
   */
  public void setMonitor(Option<IProgressMonitor> monitor) {
    this.factory.setMonitor(monitor.isNone() ? null : monitor.unwrap());
  }
 
  @Deprecated
  public LE getResultsBefore(ASTNode node) {
      return getResultsBeforeCFG(node);
    }
   
  public LE getResultsBeforeCFG(ASTNode node) {
      LE result = getResultsOrNull(node, false, false);
    return result == null ? currentLattice.bottom() : result;
    }
 
    public LE getResultsBeforeAST(ASTNode node) {
      LE result = getResultsOrNull(node, false, true);
    return result == null ? currentLattice.bottom() : result;
    }

   @Deprecated
    public LE getResultsAfter(ASTNode node) {
       return getResultsAfterCFG(node);
    }
   
    public LE getResultsAfterCFG(ASTNode node) {
      LE result = getResultsOrNull(node, true, false);
      return result == null ? currentLattice.bottom() : result;
    }
   

    public LE getResultsAfterAST(ASTNode node) {
      LE result = getResultsOrNull(node, true, true);
      return result == null ? currentLattice.bottom() : result;
    }


  /**
   * Retrieves the analysis state for a given node.
   *
   * @param node    the {@link ASTNode} of interest
   * @param getAfter  true if we want the results after analyzing the node, false if we want the results before analyzing
   * @param useAST  true if we want the results for the entire tree of this AST, false if we want the results only before/after
   *                  the given node and not any subnodes.
   * @return      the lattice that represents the analysis state
   *           before analyzing the node.  Will be <code>null</code> if the node doesn't
   *           have a corresponding control flow node.
   */
    protected LE getResultsOrNull(ASTNode node, boolean getAfter, boolean useAST) {
    if(nodeMap.containsKey(node) == false)
      performAnalysisOnSurroundingMethodIfNeeded(node);

    Set<ICFGNode<ASTNode>> cfgnodes = nodeMap.get(node);

      if(cfgnodes == null || cfgnodes.isEmpty()) {
        if(log.isLoggable(Level.FINE))
          log.fine("Unable to get results after node: " + getNodeDebugInfo(node));
        return null;
      }
      else if(cfgnodes.size() == 1) {
      ICFGNode<ASTNode> cfgNode = cfgnodes.iterator().next();
      if (getAfter)
        return getResultAfter(useAST ? cfgNode.getEnd() : cfgNode);
      else
           return getResultBefore(useAST ? cfgNode.getStart() : cfgNode);
       }
      else {
        HashMap<ICFGNode<ASTNode>, LE> results = new HashMap<ICFGNode<ASTNode>, LE>();
        for(ICFGNode<ASTNode> n : cfgnodes) {
          ICFGNode<ASTNode> resultNode;
          LE result;
          if (getAfter) {
            resultNode = useAST ? n.getEnd() : n;
            result = getResultAfter(resultNode);
          }
          else {
            resultNode = useAST ? n.getStart() : n;
            result = getResultBefore(resultNode);
          }
         
          if(result != null)
            results.put(resultNode, result);
        }
        return mergeResults(results, node);
      }
    }


    public LE getEndResults(MethodDeclaration decl) {
    if (this.currentMethod != decl) {
      performAnalysisOnSurroundingMethodIfNeeded(decl);
    }
   
    // NEB: From looking around there appear to be no
    // outgoing edges from a method declaration, which
    // is what cfgEndNode usually is. Therefore I believe
    // result should be the results before the last node.
    LE result = getResultBefore(this.cfgEndNode);
    return result == null ? currentLattice.bottom() : result;
  }

   public LE getStartResults(MethodDeclaration decl) {
    if (this.currentMethod != decl) {
      performAnalysisOnSurroundingMethodIfNeeded(decl);
    }
   
    LE result = getResultBefore(this.cfgStartNode);
    return result == null ? currentLattice.bottom() : result;
  }

  private String getNodeDebugInfo(ASTNode node) {
      return node.toString() + " type: " + ASTNode.nodeClassForType(node.getNodeType()).getName() +
        (node.getParent() != null ?
            " parent type: " + ASTNode.nodeClassForType(node.getParent().getNodeType()).getName() :
              "");
    }

    protected LE mergeResults(HashMap<ICFGNode<ASTNode>, LE> results, ASTNode node) {
      if(results.isEmpty())
        return null; // shortcut
      LE result = null;
      for(LE r : results.values()) {
        if(result == null)
          result = currentLattice.copy(r);
        else
          result = currentLattice.join(result, currentLattice.copy(r), node);
      }
    return result;
  }

    public IResult<LE> getLabeledResultsBefore(ASTNode node) {
    if(nodeMap.containsKey(node) == false)
      performAnalysisOnSurroundingMethodIfNeeded(node);

    Set<ICFGNode<ASTNode>> cfgnodes = nodeMap.get(node);

      if(cfgnodes == null || cfgnodes.isEmpty()) {
        if(log.isLoggable(Level.FINE))
          log.fine("Unable to get results before node: " + getNodeDebugInfo(node));
        return new SingleResult<LE>(currentLattice.bottom());
      }

      IResult<LE> result;
      if(cfgnodes.size() == 1) {
        result = getLabeledResultBefore(cfgnodes.iterator().next());
      }
      else {
        HashMap<ICFGNode<ASTNode>, IResult<LE>> results = new HashMap<ICFGNode<ASTNode>, IResult<LE>>();
        for(ICFGNode<ASTNode> n : cfgnodes) {
          result = getLabeledResultBefore(n);
          if(result != null)
            results.put(n, result);
        }
        result = mergeLabeledResults(results);
      }
      return result == null ? new SingleResult<LE>(currentLattice.bottom()) : result;
     }
   
    public IResult<LE> getLabeledResultsAfter(ASTNode node) {
    if(nodeMap.containsKey(node) == false)
      performAnalysisOnSurroundingMethodIfNeeded(node);

    Set<ICFGNode<ASTNode>> cfgnodes = nodeMap.get(node);

      if(cfgnodes == null || cfgnodes.isEmpty()) {
        if(log.isLoggable(Level.FINE))
          log.fine("Unable to get results after node: " + getNodeDebugInfo(node));
        return new SingleResult<LE>(currentLattice.bottom());
      }

      IResult<LE> result;
      if(cfgnodes.size() == 1) {
        result = getLabeledResultAfter(cfgnodes.iterator().next());
      }
      else {
        HashMap<ICFGNode<ASTNode>, IResult<LE>> results = new HashMap<ICFGNode<ASTNode>, IResult<LE>>();
        for(ICFGNode<ASTNode> n : cfgnodes) {
          result = getLabeledResultAfter(n);
          if(result != null)
            results.put(n, result);
        }
        result = mergeLabeledResults(results);
      }
      return result == null ? new SingleResult<LE>(currentLattice.bottom()) : result;
     }

  public IResult<LE> getLabeledEndResult(MethodDeclaration d) {
    if( this.currentMethod != d ) {
      performAnalysisOnSurroundingMethodIfNeeded(d);
    }
   
    IResult<LE> result = getLabeledResultBefore(this.cfgEndNode);
    return result == null ? new SingleResult<LE>(currentLattice.bottom()) : result;
  }

  public IResult<LE> getLabeledStartResult(MethodDeclaration d) {
    if( this.currentMethod != d ) {
      performAnalysisOnSurroundingMethodIfNeeded(d);
    }
   
    IResult<LE> result = getLabeledResultBefore(this.cfgStartNode);
    return result == null ? new SingleResult<LE>(currentLattice.bottom()) : result;
  }
   
  /**
   * Merges the given results into one, possibly <code>null</code>, result.
   * This method uses {@link IResult#join(IResult)}.
   * @param results The results to be merged.
   * @return The result of merging the given results into one, or <code>null</code>
   * if <code>results</code> is empty or only contains <code>null</code> values.
   */
  protected IResult<LE> mergeLabeledResults(
      HashMap<ICFGNode<ASTNode>, IResult<LE>> results) {
    IResult<LE> result = null;
    for(IResult<LE> r : results.values()) {
      if(result == null)
        result = r;
      else if(r != null)
        result = result.join(r, currentLattice);
    }
    return result;
  }

  /**
   * Retrieves the analysis state that exists <b>before</b> analyzing the node, if any.
   *
   * Before is respective to normal program flow and not the direction of the analysis.
   *
   * @param node    the {@link ASTNode} of interest
   * @return      the lattice that represents the analysis state
   *           before analyzing the node.  Or <code>null</code> if the node doesn't
   *           have a corresponding control flow node.
   */
    protected LE getResultBefore(ICFGNode<ASTNode> node) {
      return mergeLabeledResult(getLabeledResultBefore(node), node.getASTNode());
    }


    /**
   * Retrieves the analysis state that exists <b>after</b> analyzing the node, if any.
   *
   * After is respective to normal program flow and not the direction of the analysis.
   *
   * @param node    the {@link ASTNode} of interest
   * @return      the lattice that represents the analysis state
   *           before analyzing the node.  Or <code>null</code> if the node doesn't
   *           have a corresponding control flow node.
   */
    protected LE getResultAfter(ICFGNode<ASTNode> node) {
      return mergeLabeledResult(getLabeledResultAfter(node), node.getASTNode());
    }


    protected LE mergeLabeledResult(IResult<LE> labeledResult, ASTNode node) {
      if(labeledResult == null)
        return null;
      LE result = null;
      for(ILabel label : labeledResult.keySet()) {
        if(result == null)
          result = checkNull(currentLattice.copy(labeledResult.get(label)));
        else
          result = checkNull(currentLattice.join(result, checkNull(currentLattice.copy(labeledResult.get(label))), node));
      }
    return result;
  }

  /**
   * Retrieves the analysis state that exists <b>before</b> analyzing the node, if any.
   *
   * Before is respective to normal program flow and not the direction of the analysis.
   *
   * @param node    the {@link ASTNode} of interest
   * @return      the lattice that represents the analysis state
   *           after analyzing the node.  Or <code>null</code> if the node doesn't
   *           have a corresponding control flow node.
   */
    protected IResult<LE> getLabeledResultBefore(ICFGNode<ASTNode> node) {
      // Retrieve results for the ControlFlowNode
      if(labeledResultsBefore.containsKey(node))
        return labeledResultsBefore.get(node);
      // If results have not yet been collected, collect and retrieve again
      performAnalysisOnSurroundingMethodIfNeeded(node.getASTNode());
      if(labeledResultsBefore.containsKey(node))
        return labeledResultsBefore.get(node);
    if(log.isLoggable(Level.FINE))
      log.fine("Unable to get results after CFG node [" + node + "]");
    return null;
     }
   
    /**
   * Retrieves the analysis state that exists <b>after</b> analyzing the node, if any.
   *
   * After is respective to normal program flow and not the direction of the analysis.
   *
   * @param node    the {@link ASTNode} of interest
   * @return      the lattice that represents the analysis state
   *           after analyzing the node.  Or <code>null</code> if the node doesn't
   *           have a corresponding control flow node.
   */
    protected IResult<LE> getLabeledResultAfter(ICFGNode<ASTNode> node) {
      // Retrieve results for the ControlFlowNode
      if(labeledResultsAfter.containsKey(node))
        return labeledResultsAfter.get(node);
      // If results have not yet been collected, collect and retrieve again
      performAnalysisOnSurroundingMethodIfNeeded(node.getASTNode());
      if(labeledResultsAfter.containsKey(node))
        return labeledResultsAfter.get(node);
    if(log.isLoggable(Level.FINE))
      log.fine("Unable to get results after CFG node [" + node + "]");
    return null;
     }
   
    /**
     * Runs worklist algorithm on method the given node is part of.
     * @param node A node that must be inside a method.
     */
    private void performAnalysisOnSurroundingMethodIfNeeded(ASTNode node) {
      MethodDeclaration decl = Utilities.getMethodDeclaration(node);
      if (decl != null)
        switchToMethod(decl);
    }
   
    /**
     * Runs worklist algorithm on given method, if not already analyzed.
     * @param node A node that must be inside a method.
     *
     * Nels: Caching only works if this was the last method to be analyzed? Is there an assumption
     * that methods are analyzed in order an never returned to?
     */
    protected void switchToMethod(MethodDeclaration methodDecl) {
      if(methodDecl != currentMethod)
        performAnalysis(methodDecl);
    }
   
    private void performAnalysis(MethodDeclaration methodDecl) {
      currentMethod = methodDecl;
      WorklistTemplate<LE, ASTNode, ILatticeOperations<LE>> worklist = createWorklist(methodDecl);
      AnalysisResult<LE, ASTNode, ILatticeOperations<LE>> result = worklist.performAnalysis();
      labeledResultsBefore = result.getLabeledResultsBefore();
      labeledResultsAfter = result.getLabeledResultsAfter();
      nodeMap = result.getNodeMap();
      currentLattice = result.getLattice();
      cfgStartNode = result.getCfgStartNode();
      cfgEndNode = result.getCfgEndNode();
    }
   
    protected WorklistTemplate<LE, ASTNode, ILatticeOperations<LE>> createWorklist(MethodDeclaration methodDecl) {
      IFlowAnalysisDefinition<LE> transferFunction = createTransferFunction(methodDecl);
      if(transferFunction instanceof IBranchSensitiveTransferFunction)
        return factory.createBranchSensitiveWorklist(methodDecl, (IBranchSensitiveTransferFunction<LE>) transferFunction);
      if(transferFunction instanceof ITransferFunction)
        return factory.createBranchInsensitiveWorklist(methodDecl, (ITransferFunction<LE>) transferFunction);
      throw new IllegalStateException("Unknown type of transfer function: " + transferFunction);
  }

  protected abstract IFlowAnalysisDefinition<LE> createTransferFunction(MethodDeclaration method);

  /**
     * Returns most recently analyzed method.
     * @return Most recently analyzed method
     * or <code>null</code> if no method was analyzed yet.
     */
    protected final MethodDeclaration getCurrentMethod() {
    return currentMethod;
  }

    /**
     * Finds the method surrounding a given node and throws an exception
     * if the given node is not inside a method.
     * @param forNode
     * @return Method surrounding given node.  This method does not return
     * <code>null</code> and instead throws a <code>NullPointerException</code>.
     */
  protected static MethodDeclaration findSurroundingMethod(ASTNode forNode) {
    return checkNull(Utilities.getMethodDeclaration(forNode));
  }
 
  /**
   * This method checks whether results for the given AST node are available.
   * @param node
   * @return
   */
  protected final boolean hasResults(ASTNode node) {
    return nodeMap.containsKey(node);
  }

 
  /**
   * Returns a given object only if non-<code>null</code>; throws an exception otherwise.
   * @param <T>
   * @param o An object
   * @return The given object, if non-<code>null</code>.
   * @throws NullPointerException If the given object is null.
   */
  protected static <T> T checkNull(T o) {
    if(o == null)
      throw new NullPointerException("No value available.");
    return o;
  }
}
TOP

Related Classes of edu.cmu.cs.crystal.flow.MotherFlowAnalysis

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.