// see License.txt for copyright and terms of use

// The purpose of the Analyzer is to to investigate nsresult variable
// usage within a function.
// Annotation: variables will be annotated with isReturned, isAssigned
// Should create some sort of a path to connect rv sassignment with
// error checking.

// Utilizing Analyzer:
// Should be able ask analyzer to traverse a function
// then enumerate all of the interesting parts of the function
// such as rv assignments(linked to checks) and returns
#ifndef ANALYZER_H
#define ANALYZER_H

#include "expr_visitor.h"
#include <list>
#include <set>
#include <map>
#include "oink_global.h"

class RvAnalyzer : public ExpressionVisitor {
public:
  // container for NS_FAILED|NS_SUCCEEDED info
  typedef std::pair<E_variable*, Statement*> AssignStmt;
  struct FcallBlock {
    // this should enable some sort of SingleAssignment for cases when
    // rv is reused
    FcallBlock *previous;
    E_funCall *fcall;
    Statement *wrapper;
    typedef std::pair<E_funCall*, S_if*> FcallStmt;
    std::list<FcallStmt> nsfailed;
    std::list<FcallStmt> nssucceeded;
    std::list<S_return*> rets;
    FcallBlock(E_funCall *fcall = NULL, Statement *wrapper = NULL):
      previous(NULL), fcall(fcall), wrapper(wrapper) {
    }

    bool isTransitive() const {
      return nsfailed.empty() && rets.size();
    }
  };
  struct Usage {    
  public:
    std::list<FcallBlock> nsassignFuncs;
    std::list<AssignStmt> nsassignVars;
    std::list<Expression*> valueUsed;
  };

  RvAnalyzer(bool in_nsresult):
    in_nsresult(in_nsresult), str_nsresult(globalStrTable("nsresult")),
    str_NS_OK(globalStrTable("NS_OK")),
    str_NS_FAILED(globalStrTable("NS_FAILED")),
    str_NS_SUCCEEDED(globalStrTable("NS_SUCCEEDED"))
  {
  }
  void analyze(S_compound *s);
  Usage *getNsResult(Variable *v);
  bool is_nsresult(TypeSpecifier *ts);
  virtual bool visitExpression(Expression *e);
  virtual void postvisitExpression(Expression *e);
  virtual bool visitStatement(Statement *e);
  virtual void postvisitStatement(Statement *e);
  virtual bool visitS_decl(S_decl *s);
  virtual bool visitE_variable(E_variable *e);
  virtual void postvisitS_return(S_return *s);
  virtual void postvisitE_funCall(E_funCall *e);
private:
  void processAssign(Expression *arg, Usage &usage, Statement *sOuter);
  bool is_nsresultFcall(Expression *e);
  void backtrace();
  // yanks stuff off the stack
  Statement* statementPeek(unsigned int offset = 0) throw (x_match);
  bool in_nsresult;
  std::list<Statement*> statementStack;
  std::list<Expression*> expressionStack;
  const StringRef str_nsresult;
  const StringRef str_NS_OK;
  const StringRef str_NS_FAILED;
  const StringRef str_NS_SUCCEEDED;
  std::set<Statement*> processedStatements;
public:
  // if Statement* field != null means we can nuke the statement
  typedef std::pair<Expression*, Statement*> NS_OKinfo;
  std::list<NS_OKinfo> returnNS_OKs;
  // bool here signified that value should be wrapped
  typedef std::pair<S_return*, bool> ReturnWrapped;
  std::list<ReturnWrapped> returns;
  typedef std::map<Variable*, Usage> rvmap;
  rvmap nsresultVars;
  std::list<E_funCall*> ignoredErrors;
  // if(!foo) return NS_ERROR...
  // also if (NS_FAILED(rv) return rv
  std::list<S_if*> automaticErrors;
};

template<class T> bool setContains(std::set<T*> const &set, T *item) {
  return set.find(item) != set.end();
}

inline Expression* getCtorArg(Statement *s) 
  throw (x_match)
{
 return s->asS_expr()->expr->expr->asE_constructor()->args->first()->expr;
}

#endif //ANALYZER_H
