/*******************************************************************\

Module: Symbolic Execution

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#ifndef CPROVER_GOTO_SYMEX_GOTO_SYMEX_H
#define CPROVER_GOTO_SYMEX_GOTO_SYMEX_H

#include <std_types.h>
#include <goto-programs/goto_functions.h>

#include "basic_symex.h"

class goto_symext:
  public basic_symext
{
public:
  goto_symext(
    const namespacet &_ns,
    contextt &_new_context,
    symex_targett &_target):
    basic_symext(_ns, _new_context, _target),
    total_claims(0),
    remaining_claims(0),
    guard_identifier("goto_symex::\\guard")
  {
    options.set_option("assertions", true);
    call_stack.push_back(framet());
  }
  
  virtual void symex_start(
    const goto_functionst &goto_functions)
  {
    goto_functionst::function_mapt::const_iterator it=
      goto_functions.function_map.find("main");

    if(it==goto_functions.function_map.end())
      throw "main symbol not found";
    
    symex_start(goto_functions, it->second.body);
  }
  
  virtual void symex_start(
    const goto_functionst &goto_functions,
    const goto_programt &goto_program);

  virtual void symex(
    const goto_functionst &goto_functions,
    statet &state);

  virtual void symex_goto(statet &state, bool taken);
  
protected:
  friend class symex_dereference_statet;
  
  void new_name(symbolt &symbol);

  // statistics
  unsigned total_claims, remaining_claims;

  void dereference(
    exprt &expr,
    statet &state);
  
  void dereference_rec(
    exprt &expr,
    guardt &guard,
    class dereferencet &dereference);
  
  // guards
  
  irep_idt guard_identifier;
  
  // symex

  virtual void symex_goto(statet &state);

  virtual void symex_return(statet &state);

  virtual void symex_other(
    const goto_functionst &goto_functions,
    statet &state);
    
  virtual void claim(
    exprt &expr,
    const std::string &msg,
    statet &state);
    
  // gotos
  
  typedef std::list<statet> state_listt;
  typedef std::map<goto_programt::const_targett, state_listt> state_mapt;

  void merge_gotos(statet &state);
  
  void merge_value_sets(
    const statet &src,
    statet &dest);
      
  void phi_function(
    const statet &goto_state,
    statet &state);
  
  virtual bool get_unwind(
    const symex_targett::sourcet &source,
    unsigned unwind);
  
  virtual void loop_bound_exceeded(statet &state, const exprt &guard);
  
  // function calls
  
  friend class symex_functioncall_statet;
  
  virtual void no_body(const irep_idt &identifier)
  {
  }

  virtual void symex_functioncall(
    const goto_functionst &goto_functions,
    statet &state,
    const exprt &lhs,
    const exprt &call);

  virtual void symex_functioncall_symbol(
    const goto_functionst &goto_functions,
    statet &state,
    const exprt &lhs,
    const exprt &call);

  virtual void symex_functioncall_code(
    const goto_functionst &goto_functions,
    statet &state,
    const exprt &lhs,
    const exprt &call);
    
  virtual bool get_unwind_recursion(
    const irep_idt &identifier,
    unsigned unwind);

  void parameters(
    const code_typet &function_type,
    statet &saved_state,
    statet &state,
    const exprt &call);

  void locality(
    unsigned frame_counter,
    statet &state,
    const goto_functiont &goto_function);

  void add_end_of_function(
    exprt &code,
    const irep_idt &identifier);
                           
  std::map<irep_idt, unsigned> function_unwind;
  std::map<irep_idt, unsigned> function_frame;
  std::map<symex_targett::sourcet, unsigned> unwind_map;
  
  class framet
  {
  public:
    state_mapt state_map;
    statet state;

    bool return_point_set;
    goto_programt::const_targett return_point;
    exprt return_value;  
    
    framet():
      return_point_set(false),
      return_value((const exprt &)nil_rep)
    {
    }
  };
  
  typedef std::list<framet> call_stackt;
  call_stackt call_stack;
  
  framet &top() { return call_stack.back(); }

  // dynamic stuff  
  virtual void replace_dynamic_allocation(const statet &state, exprt &expr);
  bool is_valid_object(const statet &state, const symbolt &symbol);
};

#endif
