// 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 <sstream>
using std::stringstream;
using std::ostringstream;

#include "JoinPointLoc.h"
#include "Naming.h"
#include "Puma/DeducedArgument.h"
#include "Puma/CTemplateInstance.h"
#include "Puma/CClassInfo.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CArgumentInfo.h"
#include "Puma/CAttributeInfo.h"
#include "Puma/CTypeInfo.h"
#include "Puma/MacroUnit.h"
#include "Puma/CTree.h"
#include "Puma/Filter.h"

void JoinPointLoc::signature (ostream &sig, CFunctionInfo *func_info) {
  ostringstream name;
  if (func_info->isMethod () && !func_info->isStaticMethod ())
    name << func_info->Name ();
  else
    name << func_info->QualName ();
    
  // add the template arguments if it is a function template instance
  CTemplateInstance *instance = func_info->TemplateInstance ();
  if (instance) {
    name << "< ";
    for (unsigned a = 0; a < instance->DeducedArgs (); a++) {
      if (a > 0) name << ",";
      DeducedArgument *arg = instance->DeducedArg (a);
      if (arg->Type ())
        name << *arg->Type ();
      else if (arg->Value ()) {
        if (arg->Value ()->isSigned ())
          name << arg->Value ()->convert_to_int ();
        else if (arg->Value ()->isUnsigned ())
          name << arg->Value ()->convert_to_uint ();
        else if (arg->Value ()->isFloat ())
          name << arg->Value ()->convert_to_float ();
      }
      else
        name << "*invalid template arg*";
    }
    name << " >";
  }
  name << ends;
  func_info->TypeInfo ()->TypeText (sig, name.str ().c_str ());
  sig << ends;
}

void JoinPointLoc::signature (ostream &sig, CAttributeInfo *attr_info)
 {
   // print the type
   CTypeInfo *type = attr_info->TypeInfo ();
   type->TypeText (sig);
 
   // print a space and the qualified function name
   sig << " " << attr_info->QualName ();
   sig << ends;
 }

void JoinPointLoc::signature (ostream &sig, CTypeInfo *type_info)
 {
   // print the type
   type_info->TypeText (sig);
   sig << ends;
 }

int JoinPointLoc::line () const {
  return tree ()->token ()->location ().line ();
}

int JoinPointLoc::lines () const {
  return tree ()->end_token ()->location ().line () - line () + 1;
}

// find the unit (non-macro) in which a join point is located
Unit *JoinPointLoc::unit () const {
  CTree *node = tree ();
  if (!node)
    return 0;
  Token *token = node->token ();
  Unit *unit   = (Unit*)token->belonging_to ();
  while (unit && unit->isMacroExp ()) {
    unit = ((MacroUnit*)unit)->CallingUnit ();
  }
  return unit;
}


JPL_Code::~JPL_Code () {
  for (vector<JPL_Type*>::iterator iter = _arg_types.begin ();
       iter != _arg_types.end (); ++iter)
    delete *iter;
}

CTypeInfo *JPL_Code::get_that_type (CObjectInfo *obj) {
  CFunctionInfo *func = obj->FunctionInfo ();
  if (func) {
    CObjectInfo *this_obj = func->Attribute ("this");
    if (this_obj) {
      CTypeInfo *type = this_obj->TypeInfo ();
      // find the type which is referenced by 'this'
      while (type && !type->isRecord ())
        type = type->BaseType ();
      assert (type);
      return type;
    }
  }
  if (obj->ClassScope ())
    return obj->ClassScope ()->TypeInfo ();
  return &CTYPE_VOID;
}


// generates the signature of a wrapper function for exec/cons/dest join pts
string JPL_Code::wrapper_function_signature (CFunctionInfo *func, bool def) {
  assert (func);
  
  stringstream name_args;
  if (func->CObjectInfo::QualifiedScope () &&
      func->Scope () != func->CObjectInfo::QualifiedScope ())
    name_args << func->CObjectInfo::QualifiedScope ()->QualName () << "::";
  Naming::exec_inner (name_args, this);
  name_args << "(";
  for (unsigned a = 0; a < func->Arguments (); a++) {
    CArgumentInfo *arg = func->Argument (a);
    if (arg->TypeInfo ()->is_void ())
      break;
    if (a > 0) name_args << ",";
//    if (def)
      arg->TypeInfo ()->TypeText (name_args,
                                  arg->isAnonymous () ? "" : arg->Name (),
                                  true, true);
//    else
//      name_args << *arg->TypeInfo ();
  }
  name_args << ")" << ends;

  ostringstream wrapped;

  wrapped << "inline "; // the wrapped function should always be inlined
  if (func->isStaticMethod () && func->ClassScope () == func->Scope ())
    wrapped << "static ";

  if (func->isConstructor () || func->isDestructor ())
    wrapped << "void " << name_args.str ().c_str ();
  else {
    CTypeInfo *type = func->isConversion () ? func->ConversionType () :
                                              func->TypeInfo ()->BaseType ();
//    if (def)
      type->TypeText (wrapped, name_args.str ().c_str (), true, true);
//    else
//      type->TypeText (wrapped, name_args.str ().c_str ());
    if (func->TypeInfo ()->isConst ())
      wrapped << " const";
    if (func->TypeInfo ()->isVolatile ())
      wrapped << " volatile";
  }
  
  return wrapped.str ();
}

JPL_Method::JPL_Method (CFunctionInfo *f) : _func_info (f) {
  stringstream sig_stream;
  signature (sig_stream, f);
  _sig = sig_stream.str ();
  _lid = -1;
  CTypeInfo *rtype = (f->isConversion ()) ? f->ConversionType() :
    f->TypeInfo ()->BaseType ();
  result_type (rtype);
  // argument types are the types function function declaration
  CTypeList *arg_types = f->TypeInfo ()->ArgTypes ();
  for (unsigned a = 0; a < arg_types->Entries (); a++)
    add_arg_type (arg_types->Entry (a));     
}

CTree *JPL_Method::tree () const {
  return _func_info->Tree ();
}

Token *JPL_Method::insertloc_tjp_struct() {
  CT_FctDef *fctdef = (CT_FctDef*)_func_info->Tree ();
  return fctdef->Linkage () ? fctdef->Linkage ()->token () : fctdef->token ();
}

CTypeInfo *JPL_Method::target_type () const {
  CRecord *scope = _func_info->ClassScope ();
  return scope ? scope->TypeInfo () : &CTYPE_VOID;
}

CTypeInfo *JPL_Method::that_type () {
  return get_that_type (_func_info);
}


JPL_MethodCall::JPL_MethodCall (CFunctionInfo *called, CT_CallExpr *ce, 
				CObjectInfo *caller, int local_id, bool mi) : 
  called_func (called), caller_obj (caller),
  node (ce), _member_init (mi) {
  stringstream sig_stream;
  signature (sig_stream, called_func);
  _sig = sig_stream.str ();
  _lid = local_id;
  CTypeInfo *rtype = (called->isConversion ()) ? called->ConversionType() :
    called->TypeInfo ()->BaseType ();
  result_type (rtype);
  // Argument types are the types function function declaration.
  CTypeList *formal_arg_types = called->TypeInfo ()->ArgTypes ();
  int args = (int)formal_arg_types->Entries ();
  // For functions with default arguments, not more than the number of args
  // in the call expression is used.
  if (ce && ce->Arguments ()) {
    CT_ExprList *current_arg_list = ce->Arguments ();
    if (current_arg_list->Entries () < args) {
      args = current_arg_list->Entries ();
    }
  }
  // take the arguments types from the formal argument list
  bool have_ellipsis = false;
  int a = 0;
  for (; a < args; a++) {
    CTypeInfo *arg_type = formal_arg_types->Entry (a);
    if (arg_type->is_ellipsis ()) {
      have_ellipsis = true;
      break;
    }
    else {
      add_arg_type (arg_type);
    }
  }
  
  // For functions with variable argument lists, the types that match '...'
  // in the declaration are taken from the call expression
  if (have_ellipsis && ce && ce->Arguments ()) {
    CT_ExprList *current_arg_list = ce->Arguments ();
    for (; a < current_arg_list->Entries (); a++) {
      CTypeInfo *arg_type =
        ((CT_Expression*)current_arg_list->Entry (a))->Type ();
      add_arg_type (arg_type);
    }
  }
}

bool JPL_MethodCall::is_qualified () const {
  CTree *expr = node->Expr ();
  const char *nodename = expr->NodeName ();
  if (nodename == CT_MembPtrExpr::NodeId () || 
      nodename == CT_MembRefExpr::NodeId ())
    expr = expr->Son (2); // some access function in PUMA missing!
  nodename = expr->NodeName ();
  return nodename == CT_QualName::NodeId () || 
         nodename == CT_RootQualName::NodeId ();
}


bool JPL_MethodCall::needs_rights () const {
  // get the target object type
  CTypeInfo *type = target_type ()->UnqualType ();
  
  // no member function => no accessibility problem
  if (type->isVoid ())
    return false;
    
  // static member => no problem only if public
  if (called_func->isStaticMethod ())
    return (called_func->Protection () != CProtection::PROT_PUBLIC);
    
  // normal member function => look up the accessibility
  if (type->ClassInfo () &&
    type->ClassInfo ()->Accessibility (called_func) == CProtection::PROT_PUBLIC)
    return false;
  
  return true;
}

CTree *JPL_MethodCall::tree () const {
  return node;
}

Token *JPL_MethodCall::insertloc_tjp_struct() {
  if (caller_obj->FunctionInfo ()) {
    CT_FctDef *fctdef = (CT_FctDef*)caller_obj->Tree ();
    return fctdef->Linkage () ? fctdef->Linkage ()->token () : fctdef->token ();
  }
  else
    return ((CT_InitDeclarator*)caller_obj->Tree ())->ObjDecl ()->token ();
}

Token *JPL_MethodCall::insertloc_before () {
  if (caller_obj->FunctionInfo ())
    return caller_obj->Tree ()->token ();
  else
    return ((CT_InitDeclarator*)caller_obj->Tree ())->ObjDecl ()->token ();
}

Token *JPL_MethodCall::insertloc_after () {
  if (caller_obj->FunctionInfo ())
    return caller_obj->Tree ()->end_token ();
  else
    return ((CT_InitDeclarator*)caller_obj->Tree ())->ObjDecl ()->end_token ();
}

// the target object of the call or NULL
CT_Expression *JPL_MethodCall::target_expr (bool &is_ptr) const {

  // check if this call has a target object
  if (!(called_func->isMethod () && !called_func->isStaticMethod ()))
    return 0;
    
  CTree *result;

  // what kind of node was used for the call?
  const char *calltype = CallExprNode ()->Son(0)->NodeName ();
  
  // in most case we have no pointer
  is_ptr = false;

  if (called_func->isConversion ()) {
    // <target>
    result = CallExprNode ()->Son (0);
  }
  else if (calltype == CT_MembRefExpr::NodeId ()) {
    // <target>.method()
    result = CallExprNode ()->Son (0)->Son(0);
  } else if (calltype == CT_MembPtrExpr::NodeId ()) {
    // <target-ptr>->method()
    is_ptr = true;
    result = CallExprNode ()->Son (0)->Son (0);
  } else if (calltype == CT_BinaryExpr::NodeId ()) {
    // <target> <op> <arg>
    result = CallExprNode ()->Son (0)->Son (0);
  } else if (calltype == CT_UnaryExpr::NodeId ()) {
    // <op> <target>
    result = CallExprNode ()->Son (0)->Son (1);
  } else if (calltype == CT_PostfixExpr::NodeId ()) {
    // <target> <op>
    result = CallExprNode ()->Son (0)->Son (0);
  } else if (calltype == CT_IndexExpr::NodeId ()) {
    // <target> [ <index> ]
    result = CallExprNode ()->Son (0)->Son (0);
  } else {
    // method()
    is_ptr = true; // here 'this' is passed implicitly
    result = 0;
  }
  return (CT_Expression*)result;
}
      
CTypeInfo *JPL_MethodCall::target_type () const {
  bool is_ptr;
  CTree *expr = target_expr (is_ptr);
  if (expr) {
    CTypeInfo *type = expr->Type ();
    // if this is a pointer or reference, take the base type
    while (type && (type->TypeAddress () || !type->isRecord ()))
      type = type->BaseType ();
    assert (type);
    return type;
  }
  else if (called_func->isMethod ()) {
    if (called_func->isStaticMethod ()) {
      return called_func->ClassScope ()->TypeInfo ();
    }
    else {
      if (caller_obj->FunctionInfo ()) {
        assert (caller_obj->ClassScope ());
        return caller_obj->ClassScope ()->TypeInfo ();
      }
      else
        return &CTYPE_VOID;
    }
  }
  else
    return &CTYPE_VOID;
}

CTypeInfo *JPL_MethodCall::that_type () {
  return get_that_type (caller_obj);
}


JPL_FieldReference::JPL_FieldReference (CAttributeInfo *a,
					CTree *f, int local_id) :
   attr (a), field (f)
 {
  stringstream sig_stream;
   signature (sig_stream, attr);
  _sig = sig_stream.str ();
    _lid = local_id;
 }

CTree *JPL_FieldReference::tree () const {
  return field;
}

CTypeInfo *JPL_FieldReference::target_type () const {
  CRecord *scope = attr->ClassScope ();
  return scope ? scope->TypeInfo () : &CTYPE_VOID;
}

CTypeInfo *JPL_FieldReference::that_type () {
  // TODO: missing attribute in class definition
  return &CTYPE_VOID;
}


JPL_FieldAssignment::JPL_FieldAssignment (CAttributeInfo *a,
					  CT_CallExpr *as,
					  CTree *f, int local_id) :
   attr (a), assignment (as), field (f)
 {
  stringstream sig_stream;
   signature (sig_stream, attr);
  _sig = sig_stream.str ();
   _lid = local_id;
 }

CTree *JPL_FieldAssignment::tree () const {
  return assignment;
}

CTypeInfo *JPL_FieldAssignment::target_type () const {
  CRecord *scope = attr->ClassScope ();
  return scope ? scope->TypeInfo () : &CTYPE_VOID;
}

CTypeInfo *JPL_FieldAssignment::that_type () {
  // TODO: missing attribute in class definition
  return &CTYPE_VOID;
}


JPL_Construction::JPL_Construction (CFunctionInfo *f) : _func_info (f) {
  stringstream sig_stream;
  signature (sig_stream, f);
  _sig = sig_stream.str ();
  _lid = -1;
  result_type (f->TypeInfo ()->BaseType ());
  // argument types are the types function function declaration
  CTypeList *arg_types = f->TypeInfo ()->ArgTypes ();
  for (unsigned a = 0; a < arg_types->Entries (); a++)
    add_arg_type (arg_types->Entry (a));     
}

CTree *JPL_Construction::tree () const {
  return _func_info->ClassScope ()->Tree ();
}

Token *JPL_Construction::insertloc_tjp_struct() {
  return _func_info->isBuiltin () ? 
    ((CT_ClassDef*)_func_info->ClassScope ()->Tree ())->
    Members ()->end_token () :
    _func_info->Tree ()->token ();
}

CTypeInfo *JPL_Construction::target_type () const {
  CRecord *scope = _func_info->ClassScope ();
  return scope ? scope->TypeInfo () : &CTYPE_VOID;
}

CTypeInfo *JPL_Construction::that_type () {
  return get_that_type (_func_info);
}


JPL_Destruction::JPL_Destruction (CFunctionInfo *f) : _func_info (f) {
  stringstream sig_stream;
  signature (sig_stream, f);
  _sig = sig_stream.str ();
  _lid = -1;
  result_type (f->TypeInfo ()->BaseType ());
  // argument types are the types function function declaration
  CTypeList *arg_types = f->TypeInfo ()->ArgTypes ();
  for (unsigned a = 0; a < arg_types->Entries (); a++)
    add_arg_type (arg_types->Entry (a));     
}

CTree *JPL_Destruction::tree () const {
  return _func_info->ClassScope ()->Tree ();
}

Token *JPL_Destruction::insertloc_tjp_struct() {
  return _func_info->isBuiltin () ? 
    ((CT_ClassDef*)_func_info->ClassScope ()->Tree ())->
    Members ()->end_token () :
    _func_info->Tree ()->token ();
}

CTypeInfo *JPL_Destruction::target_type () const {
  CRecord *scope = _func_info->ClassScope ();
  return scope ? scope->TypeInfo () : &CTYPE_VOID;
}

CTypeInfo *JPL_Destruction::that_type () {
  return get_that_type (_func_info);
}


JPL_Class::JPL_Class (CClassInfo *c) : JPL_Name (c->TypeInfo ()), class_obj (c)
 {
   _sig = class_obj->QualName ();
 }

CTree *JPL_Class::tree () const {
  return class_obj->Tree ();
}

CObjectInfo *JPL_Class::link_once_object (CClassInfo *ci) {
  for (unsigned i = 0; i < ci->Functions (); i++) {
    CFunctionInfo *fi = ci->Function (i)->DefObject ();
    // skip template functions and built-in functions
    // they don't need link-once code
    if (fi->isBuiltin () || fi->isTemplate ()) {
      continue;
    }
    // if a member function is undefined it is link-once code!
    if (!fi->isDefined ()) {
      return fi;
    }
    // if the function is defined, outside the class scope, and is not inline,
    // we found the implementation
    if (fi->Scope () != ci && !fi->isInline ()) {
      return fi;
    }
  }
  for (unsigned i = 0; i < ci->Attributes (); i++) {
    CAttributeInfo *ai = ci->Attribute (i)->DefObject ();
    // if the scope is outside the class, we or definition
    if (ai->Scope () != ci) {
      return ai;
    }
    // initialized, we can us this object
    if (ai->isStatic () && !ai->Init ()) {
      return ai;
    }
  }
  return 0;
}

JPL_Function::JPL_Function (CFunctionInfo *f) :
  JPL_Name (f->TypeInfo ()), func_obj (f) {
	stringstream sig_stream;
	signature (sig_stream, func_obj);
	_sig = sig_stream.str ();
}

CTree *JPL_Function::tree () const {
  return func_obj->Tree ();
}

JPL_Type::JPL_Type (CTypeInfo *t) : JPL_Name (t)
 {
	stringstream sig_stream;
   signature (sig_stream, t);
	_sig = sig_stream.str ();
 }

CTree *JPL_Type::tree () const {
  return 0; // hm, does this mean any problems
}
