Package edu.cmu.cs.crystal.flow.concur

Source Code of edu.cmu.cs.crystal.flow.concur.ConcurrentFlowAnalysis

/**
* 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.concur;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

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

import edu.cmu.cs.crystal.flow.FlowAnalysis;
import edu.cmu.cs.crystal.flow.IBranchSensitiveTransferFunction;
import edu.cmu.cs.crystal.flow.IFlowAnalysis;
import edu.cmu.cs.crystal.flow.IResult;
import edu.cmu.cs.crystal.flow.ITransferFunction;
import edu.cmu.cs.crystal.internal.Crystal;
import edu.cmu.cs.crystal.util.Utilities;

/**
* An implementation of IFlowAnalysis that analyzes methods in
* concurrently. Creates a thread pool, and when analyzedPreemtively
* is called (or the constructor that takes a list of methods) we
* generate a future for each method declaration, force its analysis
* in a brand new FlowAnalysis object, and store that in a mapping
* from method declarations to IFlowAnalysis objects. Later, we can
* delegate the standard flow analysis methods on the future, forcing its
* completion.
*
* This class is EXPERIMENTAL because certain shared classes, most notably
* EclipseTAC, are not yet thought to be thread-safe. The biggest worry is
* that there is no memory barrier between different analyses.
*
* @author Nels Beckman
*
* @param <LE>
*/
public class ConcurrentFlowAnalysis<LE>
implements IFlowAnalysis<LE> {
 
  /**
   * Thread pool for executing concurrent flow analyses.
   */
  private ExecutorService threadPool =
    Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
 
  private final Map<MethodDeclaration, Future<IFlowAnalysis<LE>>> analyzedMethods =
    new HashMap<MethodDeclaration, Future<IFlowAnalysis<LE>>>()
 
  private final Crystal myCrystal;
  private final ITransferFunction<LE> transferFunction;
  protected final IFlowAnalysis<LE> defaultFlowAnalysis;
 
  @SuppressWarnings("unused")
  private ConcurrentFlowAnalysis() {
    throw new RuntimeException("Bug: Invalid constuctor called");
  }
 
  /**
   * Creates a new concurrent flow analysis and begins to analyze the given
   * method bodies immediately.
   * @param transferFunction The transfer function defining the analysis.
   * @param methods Starts analyzing these methods immediately in background threads.
   * @param crystal
   */
  public ConcurrentFlowAnalysis(ITransferFunction<LE> transferFunction,
      List<MethodDeclaration> methods,
      Crystal crystal) {
   
    this(transferFunction, crystal);
    analyzePreemitively(methods);
  }
 
  /**
   * Creates a new concurrent flow analysis but does not analyze any methods
   * immediately.
   * @see #analyzePreemitively
   * @param transferFunction
   * @param crystal
   */
  public ConcurrentFlowAnalysis(ITransferFunction<LE> transferFunction,
      Crystal crystal) {
    this.myCrystal = crystal;
    this.transferFunction = transferFunction;
    this.defaultFlowAnalysis = this.createNewFlowAnalysis(transferFunction, crystal);
  }
 
  /**
   * Perform dataflow analysis asynchronously on the list of methods given.
   * This method should return relatively quickly, but analysis will be
   * performed in another thread. If you have already called the constructor
   * that takes a list of methods, calling this method is not required unless
   * you have added new methods to analyze.
   *
   * @param methods
   */
  public void analyzePreemitively(List<MethodDeclaration> methods) {
    for(final MethodDeclaration decl: methods) {
      if( this.analyzedMethods.containsKey(decl) ) continue;
     
      Future<IFlowAnalysis<LE>> future =
        threadPool.submit(new Callable<IFlowAnalysis<LE>>() {
          public IFlowAnalysis<LE> call() throws Exception {
            IFlowAnalysis<LE> analyzer =
              ConcurrentFlowAnalysis.this.createNewFlowAnalysis(transferFunction, myCrystal);
            /*
             * TODO: Ugly, we are forcing an analysis by calling getresults
             * on the method declaration. This only works because of my
             * inside knowledge of how FlowAnalysis works and is not inherent
             * in the interface of the method.
             */
            analyzer.getResultsAfter(decl);
            return analyzer;
          }

        });
     
      analyzedMethods.put(decl, future);
    }
  }
 
  @SuppressWarnings("unchecked")
  private IFlowAnalysis<LE> createNewFlowAnalysis(ITransferFunction<LE> transferFunction,
      Crystal crystal) {
    /*
     * This will be called once for each method so that threads
     * will not step on each other. In ConcurrentTACFlowAnalysis,
     * hopefully we can just override this to return an TACFA.
     */
    if( transferFunction instanceof IBranchSensitiveTransferFunction ) {
      return new FlowAnalysis<LE>((IBranchSensitiveTransferFunction<LE>)transferFunction);
    }
    else {
      return new FlowAnalysis<LE>(transferFunction);
    }
  }
 
  public IResult<LE> getLabeledResultsAfter(ASTNode node) {
    /*
     * TODO: Need a find a way to get rid of this call...
     */
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getLabeledResultsAfter(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getLabeledResultsAfter(node);
  }

  public IResult<LE> getLabeledResultsBefore(ASTNode node) {
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getLabeledResultsBefore(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getLabeledResultsBefore(node);
  }

  @Deprecated
  public LE getResultsAfter(ASTNode node) {
    return getResultsAfterCFG(node);
  }

  @Deprecated
  public LE getResultsBefore(ASTNode node) {
    return getResultsBeforeCFG(node);
  }

  public LE getResultsAfterCFG(ASTNode node) {
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getResultsAfterCFG(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getResultsAfterCFG(node);
  }

  public LE getResultsBeforeCFG(ASTNode node) {
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getResultsBeforeCFG(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getResultsBeforeCFG(node);
  }

  public LE getResultsBeforeAST(ASTNode node) {
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getResultsBeforeAST(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getResultsBeforeAST(node);
  }

  public LE getResultsAfterAST(ASTNode node) {
    MethodDeclaration decl = Utilities.getMethodDeclaration(node);
   
    if( decl!= null && this.analyzedMethods.containsKey(decl) ) {
      try {
        return this.analyzedMethods.get(decl).get().getResultsAfterAST(node);
      } catch (Exception e) {/* Do nothing, let fall through to default. */ }
    }
    return addAsFakeFuture(decl,defaultFlowAnalysis).getResultsAfterAST(node);
  }


  /*
   * In an effort provide further caching, this method will add the given
   * method declaration to the analyzedMethods map immediately, without
   * attempting to perform any computation asynchronously (hence the FAKE future).
   * Can't decide if this is a good idea or not...
   */
  private IFlowAnalysis<LE> addAsFakeFuture(MethodDeclaration decl,
      final IFlowAnalysis<LE> fa) {
    this.analyzedMethods.put(decl, new Future<IFlowAnalysis<LE>>() {
      public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
      }
      public IFlowAnalysis<LE> get() throws InterruptedException, ExecutionException {
        return fa;
      }
      public IFlowAnalysis<LE> get(long timeout, TimeUnit unit)
          throws InterruptedException, ExecutionException,
          TimeoutException {
        return fa;
      }
      public boolean isCancelled() {
        return false;
      }
      public boolean isDone() {
        return true;
      }
    });
    return fa;
  }

  protected Map<MethodDeclaration, Future<IFlowAnalysis<LE>>> getAnalyzedMethods() {
    return analyzedMethods;
  }

  public LE getEndResults(MethodDeclaration d) {
    throw new UnsupportedOperationException("Unimplemented");
  }

  public IResult<LE> getLabeledEndResult(MethodDeclaration d) {
    throw new UnsupportedOperationException("Unimplemented");
  }

  public IResult<LE> getLabeledStartResult(MethodDeclaration d) {
    throw new UnsupportedOperationException("Unimplemented");
  }

  public LE getStartResults(MethodDeclaration d) {
    throw new UnsupportedOperationException("Unimplemented");
  }
}
TOP

Related Classes of edu.cmu.cs.crystal.flow.concur.ConcurrentFlowAnalysis

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.