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

Module: Pointer Logic

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

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

#include <assert.h>

#include <i2string.h>
#include <arith_tools.h>
#include <pointer_offset_size.h>

#include "pointer_logic.h"

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

Function: pointer_logict::object_expr

  Inputs:

 Outputs:

 Purpose:

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

void pointer_logict::object_expr(
  unsigned object,
  const typet &type,
  exprt &dest) const
{
  dest.make_nil();

  if(object==null_object) // NULL?
  {
    dest=exprt("constant", type);
    dest.set("value", "NULL");
    return;
  }
  else if(object==invalid_object) // INVALID?
  {
    dest=exprt("constant", type);
    dest.set("value", "INVALID");
    return;
  }

  for(object_mapt::const_iterator
      it=object_map.begin();
      it!=object_map.end();
      it++)
    if(it->second==object)
    {
      std::string result;

      const exprt &expr=it->first;
      
      dest.copy_to_operands(expr);
      dest.type()=type;

      if(type.id()=="reference")
        dest.id("reference_to");
      else if(expr.type().id()=="array")
        dest.id("implicit_address_of");
      else
        dest.id("address_of");

      return;
    }

  dest=exprt("constant", type);
  dest.set("value", "INVALID-"+i2string(object));
}

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

Function: pointer_logict::pointer_expr

  Inputs:

 Outputs:

 Purpose:

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

void pointer_logict::pointer_expr(
  const pointert &pointer,
  const typet &type,
  exprt &dest) const
{
  // first get object
  exprt object_ptr;
  
  object_expr(pointer.object, type, object_ptr);
  
  if(object_ptr.is_constant())
  {
    dest.swap(object_ptr);
    return;
  }
    
  assert(object_ptr.operands().size()==1);
  
  exprt object;
  object.swap(object_ptr.op0());
  
  // now do offset
  mp_integer offset_rest;
  pointer_expr_rec(pointer.offset, type, object, offset_rest);
  
  if(type.id()=="reference")
    dest=exprt("reference_to", typet("reference"));
  else if(type.id()=="array")
    dest=exprt("implicit_address_of", typet("pointer"));
  else
    dest=exprt("address_of", typet("pointer"));

  dest.type().subtype()=object.type();
  dest.move_to_operands(object);

  if(dest.type().id()=="pointer" &&
     offset_rest!=0)
  {
    exprt number=from_integer(offset_rest, typet("integer"));

    exprt tmp("+", dest.type());
    tmp.move_to_operands(dest, number);
    
    dest.swap(tmp);
  }  
}

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

Function: pointer_logict::pointer_expr_rec

  Inputs:

 Outputs:

 Purpose:

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

void pointer_logict::pointer_expr_rec(
  const mp_integer &offset,
  const typet &pointer_type,
  exprt &dest,
  mp_integer &offset_rest) const
{
  if(offset<0)
  {
    dest.make_nil();
    return;
  }

  offset_rest=offset;
  
  if(dest.type().id()=="array")
  {
    mp_integer size=pointer_offset_size(dest.type().subtype());
    
    if(size==0)
    {
      dest.make_nil();
      return;
    }
    
    mp_integer index=offset/size;
    mp_integer rest=offset%size;
    
    exprt index_expr=from_integer(index, typet("integer"));

    exprt tmp("index", dest.type().subtype());
    tmp.move_to_operands(dest, index_expr);

    pointer_expr_rec(rest, pointer_type, tmp, offset_rest);
    dest.swap(tmp);
  }
  else if(dest.type().id()=="struct" ||
          dest.type().id()=="union")
  {
    assert(offset>=0);
  
    if(offset==0)
    {
      // the struct itself
    }
    else
    {
      const irept::subt &components=
        dest.type().find("components").get_sub();
        
      mp_integer current_offset=1;

      assert(offset>=current_offset);

      forall_irep(it, components)
      {
        assert(offset>=current_offset);

        const typet &subtype=(typet &)it->find("type");
        mp_integer sub_size=pointer_offset_size(subtype);
        
        if(sub_size==0)
        {
          dest.make_nil();
          return;
        }
        
        mp_integer new_offset=current_offset+sub_size;

        if(new_offset>offset)
        {
          // found it
          exprt tmp("member", subtype);
          tmp.set("component_name", it->get("name"));
          tmp.move_to_operands(dest);
          
          pointer_expr_rec(offset-current_offset, pointer_type, tmp, offset_rest);
          dest.swap(tmp);
          
          break;
        }
        
        assert(new_offset<=offset);

        current_offset=new_offset;
        
        assert(current_offset<=offset);
      }
    }
  }
}

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

Function: pointer_logict::pointer_logict

  Inputs:

 Outputs:

 Purpose:

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

pointer_logict::pointer_logict()
{
  // add NULL

  {
    exprt tmp("constant", typet("pointer"));
    tmp.type().set("subtype", "empty");
    tmp.set("value", "NULL");

    null_object=add_object(tmp);
  }

  // add INVALID

  {
    exprt tmp("constant", typet("pointer"));
    tmp.type().set("subtype", "empty");
    tmp.set("value", "INVALID");

    invalid_object=add_object(tmp);
  }
}

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

Function: pointer_logict::~pointer_logict

  Inputs:

 Outputs:

 Purpose:

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

pointer_logict::~pointer_logict()
{
}
