// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//                                                                
// This program is free software;  you can redistribute it and/or 
// modify it under the terms of the GNU General Public License as 
// published by the Free Software Foundation; either version 2 of 
// the License, or (at your option) any later version.            
//                                                                
// This program 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 General Public License for more details.                   
//                                                                
// You should have received a copy of the GNU General Public      
// License along with this program; if not, write to the Free     
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
// MA  02111-1307  USA                                            

#include "TrackerDog.h"
#include "JoinPointLocList.h"
#include "IntroductionUnit.h"

#include "Puma/CFunctionInfo.h"
#include "Puma/CClassInfo.h"
#include "Puma/CRecord.h"
#include "Puma/CTree.h"

CObjectInfo *TrackerDog::unique_object (CObjectInfo *obj) {
  unsigned long fpos = 0L;
  CObjectInfo *first = 0;
  CObjectInfo *curr = obj;
  do {
    // builtin objects might have a declaration, but the generated obj is ret.
    if (curr->isBuiltin ())
      return curr;
    CTree *tree = curr->Tree ();
    unsigned long cpos = tree->token_node ()->Number ();
    // if the current node is not a forward declaration, it will be returned
    if (tree->NodeName () == CT_ClassDef::NodeId () ||
        tree->NodeName () == CT_FctDef::NodeId ())
      return curr;
    // check if the current forward declaration was the first
    if (!first || cpos < fpos) {
      fpos = cpos;
      first = curr;
    }
    curr = curr->NextObject ();
  } while (curr != obj);
  // if there were only forward declarations, return the first
  return first;  
}


CFunctionInfo *TrackerDog::filter_func (CFunctionInfo* fi) {
  CRecord *cls = fi->ClassScope ();
  
  if (fi->isBuiltin () || fi->isTemplate () ||
      (cls && cls->isTemplate ()) ||
      strncmp (fi->Name (), "%a", 2) == 0 ||
      strncmp (fi->Name (), "__a", 3) == 0 ||
      strcmp (fi->Name (), "aspectof") == 0 ||
      strcmp (fi->Name (), "aspectOf") == 0 ||
      fi != unique_object (fi))
    return 0;
  
  if (strcmp (fi->SourceInfo ()->FileName (), "<anonymous unit>") == 0)
    return 0;
  
  if (cls &&
      (strcmp (cls->QualName (), "__JoinPoint") == 0 ||
       strcmp (cls->QualName (), "AC::Action") == 0))
    return 0;

  CClassDatabase &db = *(CClassDatabase*)fi->ClassDB();
  if (db.PointcutInfo (fi))
    return 0;

  return fi;
}

void TrackerDog::pseudo_jps (JoinPointLocList &jpll) {
  for (unsigned f = 0; f < db ().FunctionInfos (); f++) {
    CFunctionInfo *fi = db ().FunctionInfo (f);
    if ((fi = filter_func (fi)) != 0) {
      if (!fi->isPureVirtual () &&
          (db ().Project ()->isBelow (fi->SourceInfo ()->SrcUnit ()) ||
           IntroductionUnit::cast (fi->SourceInfo ()->SrcUnit ()))) {
        if (fi->isConstructor ()) {
          jpll.new_construction (fi);
        }
        else if (fi->isDestructor ()) {
          jpll.new_destruction (fi);
        }
        else
          jpll.new_method (fi);

        // constructors and and destructors cannot be called
        if (!(fi->isConstructor () || fi->isDestructor ()))
          jpll.new_method_call (fi, 0, 0, 0);
      }
    }
  }

  // now track down special implicitly declared member functions
  for (unsigned int c = 0; c < db ().ClassInfos (); c++) {
    CClassInfo *ci = db ().ClassInfo (c);

    // Don't consider anonymous classes like template instances here
    if (strncmp (ci->Name (), "%", 1) == 0)
      continue;

    ci = (CClassInfo*)TrackerDog::unique_object (ci);
    if (ci) {
      for (unsigned f = 0; f < ci->Functions (); f++) {
        // there is no construction/destruciton jp associated with a struct
        if (ci->isStruct ())
          continue;
          
        CFunctionInfo *fi = ci->Function (f);
        if (!fi->isBuiltin ())
          continue;
        if (fi->isConstructor ())
          jpll.new_construction (fi);
        else if (fi->isDestructor ())
          jpll.new_destruction (fi);
      }
    }
  }
}

void TrackerDog::pre_visit (CTree *node)
 {
   bool keep_assignment = false;
   const char *id = node->NodeName ();

   if (id == CT_Token::NodeId ())
      return;

   if (id == CT_PointcutDecl::NodeId ())
      pre_PointcutDecl ((CT_PointcutDecl *)node);
   else if (id == CT_AdviceDecl::NodeId ())
      pre_AdviceDecl ((CT_AdviceDecl *)node);
   else if (id == CT_CallExpr::NodeId ())
    {
      pre_CallExpr ((CT_CallExpr *)node);
      keep_assignment = true;
    }
   else if (id == CT_FctDef::NodeId ())
      pre_FctDef ((CT_FctDef *)node);

//    else if (id == CpppAst_MemPtrExpr::nodeId ())
//       pre_MemPtrExpr ((CpppAst_MemPtrExpr *)node);
//    else if (id == CpppAst_MemRefExpr::nodeId ())
//       pre_MemRefExpr ((CpppAst_MemRefExpr *)node);
//    else if (id == CpppAst_SimpleName::nodeId ())
//       pre_SimpleName ((CpppAst_SimpleName *)node);
//    else if (id == CpppAst_QualName::nodeId ())
//       pre_QualName ((CpppAst_QualName *)node);
//    else if (id == CpppAst_RootQualName::nodeId ())
//       pre_RootQualName ((CpppAst_RootQualName *)node);
    else if (id == CT_InitDeclarator::NodeId ())
       pre_InitDeclarator ((CT_InitDeclarator*)node);
    else if (id == CT_MembInitList::NodeId ())
       in_memb_init = true;;
//    else if (id == CpppAst_BracedExpr::nodeId () ||
// 	    id == CpppAst_BuiltinCast::nodeId () ||
// 	    id == CpppObject_BuiltinFunction::nodeId ())
//       keep_assignment = true;

//    if (!keep_assignment)
//       assignment = (CpppAst_CallExpr*)0;
 }

void TrackerDog::post_visit (CTree *node) {
  const char *id = node->NodeName ();
  if (id == CT_FctDef::NodeId ())
    post_FctDef ((CT_FctDef *)node);
  else if (id == CT_InitDeclarator::NodeId ())
    post_InitDeclarator ((CT_InitDeclarator *)node);
  else if (id == CT_MembInitList::NodeId ())
    in_memb_init = false;
}

void TrackerDog::pre_PointcutDecl (CT_PointcutDecl *node)
 {
   // we don't have to find calls inside of aspect code here!
   prune ();
 }

void TrackerDog::pre_AdviceDecl (CT_AdviceDecl *node)
 {
   // we don't have to find calls inside of aspect code here!
   prune ();
 }

void TrackerDog::pre_CallExpr (CT_CallExpr *node) {
  // ignore calls via function pointer, which cannot be resolved
  if (!node->Object ())
     return;
   
  CFunctionInfo *called = node->Object ()->FunctionInfo ();
  called = (CFunctionInfo*)unique_object (called);

  // conditions to ignore this call (join point):
  // - constructor/destructor calls (not yet implemented)
  // - calls to generated or special ac++ code
  // - the calling code is not part of the project
  if (called->isConstructor () || called->isDestructor () ||
      !filter_func (called) ||
      !db ().Project ()->isBelow ((Unit*)node->token ()->belonging_to ()))
    return;

  CObjectInfo *caller = func_stack.top ();
  if (caller) {
    // call inside a function
    _world.new_method_call (called, node, caller->DefObject (), local_id,
			    in_memb_init);
    local_id++;
  }
  else if (last_init_declarator) {
    // call outside a function: this must be a declaration in global scope with initializer
    caller = last_init_declarator->Object ();
    _world.new_method_call (called, node, caller->DefObject (), -1);
  }
  else {
    // here we must have a caller, otherwise there is a strange kind of call
    assert(false);
  }
}

void TrackerDog::pre_FctDef (CT_FctDef *node)
 {
   CObjectInfo *func = node->Object ();

   if (func && func->FunctionInfo ())
    {
      func_stack.push (func->FunctionInfo ());
      local_id = 0;
    }
   else
      prune ();
 }

void TrackerDog::post_FctDef (CT_FctDef *node)
 {
   func_stack.pop ();
 }

// void TrackerDog::pre_MemPtrExpr (CpppAst_MemPtrExpr *node)
//  {
//    if (get_or_set (node, (CpppAst_SimpleName *)node->field ()))
//     {
//       visit (node->object ());
//       prune ();
//     }
//  }

// void TrackerDog::pre_MemRefExpr (CpppAst_MemRefExpr *node)
//  {
//    if (get_or_set (node, (CpppAst_SimpleName *)node->field ()))
//     {
//       visit (node->object ());
//       prune ();
//     }
//  }

// void TrackerDog::pre_SimpleName (CpppAst_SimpleName *node)
//  {
//    get_or_set (node, node);
//  }

// void TrackerDog::pre_QualName (CpppAst_QualName *node)
//  {
//    get_or_set (node, node->nameAst ());
//    prune ();
//  }

// void TrackerDog::pre_RootQualName (CpppAst_RootQualName *node)
//  {
//    get_or_set (node, node->nameAst ());
//    prune ();
//  }

void TrackerDog::pre_InitDeclarator (CT_InitDeclarator *node) {
  last_init_declarator = node;
}

void TrackerDog::post_InitDeclarator (CT_InitDeclarator *node) {
  last_init_declarator = 0;
}

// bool TrackerDog::get_or_set (CpppAstInfo *node, CpppAst_SimpleName *field)
//  {
//    TN_AttributeInfo *attr = attribute (field);

//    if (!attr)
//       return false;

//    JoinPoint::join_point_type tp;
//    JoinPointLoc *jpl;

//    if (assignment)
//     {
//       jpl = aspectdb->new_FieldAssignment (attr, assignment, node, local_id);
//       tp = JoinPoint::FieldAssignment;
//     }
//    else
//     {
//       jpl = aspectdb->new_FieldReference (attr, node, local_id);
//       tp = JoinPoint::FieldReference;
//     }

//    aspectdb->expand_world (new JoinPoint (tp, jpl, 0));
//    local_id++;

//    return true;
//  }

// TN_AttributeInfo *TrackerDog::attribute (CpppAst_SimpleName *name)
//  {
//    name = name->nameAst ();

//    if (!name->refObject ())
//       return (TN_AttributeInfo *)0;

//    if (!(name->refObject ()->astName () == CpppObject_Variable::nodeId ()))
//       return (TN_AttributeInfo *)0;

//    CpppScopeClass scl = name->refObject ()->defScope ()->scopeClass ();
//    if (scl != CPPP_SCOPE_EXTERN && scl != CPPP_SCOPE_FILE &&
//        scl != CPPP_SCOPE_CLASS && scl != CPPP_SCOPE_UNION)
//       return (TN_AttributeInfo *)0;
      
//    TN_ObjectInfo *obj = aspectdb->ObjectInfo (~(CpppObject)name->
// 					      refObject ());
//    if (!obj || !obj->AttributeInfo ())
//       return (TN_AttributeInfo *)0;

//    return obj->AttributeInfo ();
//  }

// void TrackerDog::dump_node (CpppAstInfo *node)
//  {
//    Token *token = node->token ();
//    Token *end_token = node->end_token ();
//    if (token)
//       cout << token->location () << ": ";
//    cout << node->astName () << " " << (void*)node;

//    if (token)
//       cout << " from '" << token->text () << "'";
//    if (end_token)
//       cout << " to '" << end_token->text () << "'.";
//    cout << endl;
//  }

