// 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 <assert.h>

#include "CodeGenerator.h"
#include "AspectInfo.h"
#include "AdviceInfo.h"
#include "Naming.h"
#include "TransformInfo.h"
#include "ThisJoinPoint.h"
#include "ACResultBuffer.h"

#include "clang/Frontend/CompilerInstance.h"

using namespace std;

// helper function that find the unit in which an advice code is defined
ACFileID CodeGenerator::aspect_unit (ACM_Aspect *a) const {
  clang::SourceManager &sm = project_.get_compiler_instance ()->getSourceManager ();
  clang::SourceLocation loc = TransformInfo::decl(*a)->getLocation();
  if (loc.isMacroID())
    loc = sm.getExpansionLoc(loc);
  clang::FileID fid = sm.getFileID(loc);

#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_18_1_3
  return sm.getFileEntryRefForID (fid);
#else
  return sm.getFileEntryForID (fid);
#endif
}

// generate the invocation function forward declarations for an aspect
string CodeGenerator::aspect_ifct_decls (const AspectInfo &aspect_info) const {
  // name of this aspect
  string me (aspect_info.name());
  string decls = string ("\n") +
    "#ifdef __cplusplus\n"
    "#ifndef __ac_fwd_" + me + "__\n"
    "#define __ac_fwd_" + me + "__\n"
    "extern \"C++\" {\n"
    "class " + me + ";\n"
    "namespace AC {\n";
  stringstream ifct_decls;
  for (auto advice_info : aspect_info.advice_infos()) {
    advice_invocation_func(ifct_decls, *advice_info, false);
  }
  decls += ifct_decls.str ();
  decls += "}\n";
  decls += "}\n";
  decls += "#endif\n";
  decls += "#endif\n\n";
  return decls;
}

// generate the invocation function definitions
string CodeGenerator::aspect_ifct_defs (const AspectInfo &aspect_info) const {
  stringstream defs;
  defs << endl;
  defs << "namespace AC {" << endl;
  for (auto advice_info : aspect_info.advice_infos()) {
    advice_invocation_func(defs, *advice_info, true);
  }
  defs << "} " << endl;
  return defs.str ();
}

// generate the forward decls for an aspect and its advice for a given file
string CodeGenerator::aspect_refs (const AspectRefSet &set) const {
  string includes;
  // const AspectRefSet &set = (*entry).second;
  for (AspectRefSet::const_iterator iter = set.begin ();
    iter != set.end (); ++iter) {

    switch ((*iter)._kind) {
      case AspectRef::AR_DECL: 
        includes += "class ";
        includes += (*iter)._aspect->name ();
        includes += ";\n";
        break;
      case AspectRef::AR_ADVICE:
        {
          ACFileID unit = aspect_unit (&(*iter)._aspect->loc ());
          includes += aspect_ifct_decls(*((*iter)._aspect));
          stringstream define;
          define << "#ifndef __ac_need_";
          Naming::mangle_file (define, /*(Puma::FileUnit*)*/unit);
          define << endl << "#define __ac_need_";
          Naming::mangle_file (define, /*(Puma::FileUnit*)*/unit);
          define << endl << "#endif" << endl;
          includes += define.str ();          
        }
        break;
      default:
        cout << "aspect ref type not yet implemented" << endl;
        assert(false);
    }
  }
  return includes;
}

// generate the JoinPoint data structure for a give code joinpoint
void CodeGenerator::tjp_struct (std::ostream &code, const ThisJoinPoint &tjp,
  ACM_Code *loc, int depth) const {

  // the level 0 struct -> contains all the data and types
  code << "template <typename TResult, typename TThat, typename TTarget, ";
  // Entity is only supported in the Clang variant and if enabled on cmdline
  if( tjp.entity_enabled() ) {
    code << "typename TEntity, ";
    if( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) )
      code << "typename TDims, ";
  }
  code << "typename TArgs";
  if( tjp.has_wormhole() )
    code << ", typename TWORMHOLE";
  code << "> struct ";
  Naming::tjp_struct (code, loc, depth);
  if (depth > 0) {
    // the level N > 0 structs -> are derived from level N - 1
    code << " : ";
    Naming::tjp_struct (code, loc, depth - 1);
    code << "<TResult, TThat, TTarget, ";
    // Entity is only supported in the Clang variant
    if( tjp.entity_enabled() ) {
      code << "TEntity, ";
      if( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) )
        code << "TDims, ";
    }
    code << "TArgs";
    if( tjp.has_wormhole() )
      code << ", TWORMHOLE";
    code << ">";
  }
  else {
    // the level N == 0 structs are derived from AC::Action, if action() used
    if (tjp.useAction())
      code << " : AC::Action";
  }
  code << " {" << endl;
  code << "  typedef ";
  Naming::tjp_struct (code, loc, depth);
  code << " __TJP;" << endl; // internal type definition

  if( depth != 0 )
    return; // nothing to do, skip generation of lvl 0 parts

  stringstream types, data, fct;

  // start: type definitions --------------------------------------------------
  bool res_is_undef   = !has_result_type(*loc);
  bool res_is_ref     = false;
  string opt_const    = "";
  string opt_volatile = "";
  if (!res_is_undef) {
    ACM_Type *rtype = get_result_type (*loc);
    MatchSignature &match_sig = rtype->get_match_sig();
    if (match_sig.is_new())
      match_sig.parse(format_type (*rtype));
    MatchTypeRef mtr = match_sig.type ();
    if (mtr.is_reference()) {
      res_is_ref = true;
      mtr = mtr.base();
    }
    if (mtr.is_qualifiable() && mtr.qualifiers().is_const())
      opt_const = "const ";
    if (mtr.is_qualifiable() && mtr.qualifiers().is_volatile())
      opt_volatile = "volatile ";
  }
  types << "  typedef " << opt_const // << opt_volatile
    << "TResult" << ( res_is_ref ? "*" : "" ) << ( tjp.forced_const_result() ? " const" : "" ) << " Result;" << endl;
  types << "  typedef TThat   That;" << endl;
  types << "  typedef TTarget Target;" << endl;
  // this feature is only available in Clang and if enabled on cmdline
  if( tjp.entity_enabled() ) {
    types << "  typedef TEntity Entity;" << endl;
    if( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) ) {
      types << "  enum { DIMS = TDims::Count };" << endl;
      types << "  typedef typename TDims::template Array<TEntity>::Type Array;" << endl;
      types << "  template <unsigned int D> struct Dim : AC::DIL_Op<TDims, D> {};" << endl;
    }
    else
      types << "  enum { DIMS = 0 };" << endl;
    if( tjp.memberptr_defined( loc ) )
      types << "  typedef " << ( tjp.array_defined( loc ) ? "Array" : "TEntity" ) << " TTarget::* MemberPtr;" << endl;
    else // use void * for non member entities (function returns 0)
      types << "  typedef void * MemberPtr;" << endl;

    // generate EntityInfo, which refers to the introspection API
    if (has_entity_var(*loc)) { // restrict to variables, otherwise use has_entity(...)
      ACM_Name *entity = get_entity(*loc);
      if (entity) {
        ACM_Class *defining_cls = 0; // the class where the entity has been declared
        if (entity->get_parent() && ((static_cast<ACM_Node*>(entity->get_parent()))->type_val() & JPT_Class))
          defining_cls = static_cast<ACM_Class*>(entity->get_parent());

        // generate a default EntityInfo (void), for example, if the entity is unknown or if introspection code is unavailable
        types << "  template<typename T, int HAVE=AC::HasTypeInfo<T>::RET, int __D=0> struct __EntityInfo { typedef void EntityInfo; };" << endl;
        if (defining_cls) {
          // generate the actual EntityInfo
          types << "  template<typename T, int __D> struct __EntityInfo<T, 1, __D> { "
                << "typedef typename TTarget::__TI_"
                << defining_cls->get_name()
                << "::Member_" << entity->get_name()
                << " EntityInfo; };" << endl;
        }
        types << "  typedef typename __EntityInfo<TTarget>::EntityInfo EntityInfo;" << endl;
      }
    }
  }

  // argument count and types
  unsigned arg_count = get_arg_count( *loc );
  types << "  enum { ARGS = TArgs::ARGS };" << endl;
  if( tjp.forced_const_args() ) {
    types << "  template <int I> struct Arg : AC::ArgCQ<TArgs, " << tjp.forced_const_args() << ", I> {};" << endl;
  }
  else
    types << "  template <int I> struct Arg : AC::Arg<TArgs, I> {};" << endl;
    
  // join point id and type;
  // TODO: handle join-point ID in TJP
  if (tjp.id()) {
    int jpid = loc->get_jpid(); //(loc->assigned_id () == -1 ? loc->id () : loc->assigned_id ());
    types << "  static const int JPID = " << jpid << ";" << endl;
  }
  types << "  static const AC::JPType JPTYPE = (AC::JPType)";
  switch( loc->type_val() ) { // map to real ids if necessary
  case JPT_GetRef:
    types << JPT_Get; break;
  case JPT_SetRef:
    types << JPT_Set; break;
  default:
    types << loc->type_val(); break;
  }
  types << ";" << endl;
  
  // result type
  types << "  struct Res {" << endl;
  types << "    typedef " << opt_const << opt_volatile
    << (res_is_undef ? "void" : "TResult") << " "
    << (res_is_ref ? "&" : "") << "Type;" << endl;
  types << "    typedef " << opt_const << opt_volatile
    << (res_is_undef ? "void" : "TResult") << " ReferredType;" << endl;
  types << "  };" << endl;

  if( tjp.has_wormhole() )
    types << "  typedef TWORMHOLE __WORMHOLE;" << endl;
  
  // begin of static data -----------------------------------------------------
  if (tjp.signature ()) {
    fct << "  inline static const char *signature () { return \"";
    fct << ::signature (*loc) << "\";}" << endl;
  }
  if (tjp.filename ()) {
    fct << "  inline static const char *filename () { return \"";
    fct << ::filename (*loc) << "\";}" << endl;
  }
  if (tjp.line ()) {
    fct << "  inline static int line () { return ";
    fct << ::line(*loc) << ";}" << endl;
  }
  if (tjp.args ()) {
    fct << "  inline static const int args () { return ";
    fct << arg_count << ";";
    fct << " }" << endl;
  }
  if (tjp.type()) {
    fct << "  inline static  AC::Type type() { return ";
    fct << "\"";
    if (has_result_type(*loc))
      Naming::mangle (fct, get_result_type(*loc));
    list<ACM_Type*> tlist;
    get_arg_types (*loc, tlist);
    for (list<ACM_Type*>::iterator iter = tlist.begin(); iter != tlist.end();
        ++iter)
      Naming::mangle (fct, *iter);
    fct << "\"; }" << endl;
  }
  if (tjp.id()) {
    fct << "  inline static unsigned int id() { return JPID; }" << endl;
  }
  
  if (tjp.resulttype()) {
    fct << "  inline static AC::Type resulttype() {return ";
    if (has_result_type(*loc)) {
      fct << "\"";
      Naming::mangle (fct, get_result_type(*loc));
      fct << "\";";
    } else fct << "\"<unknown signature>\";";
    fct << "}" << endl;    
  }
  
  if (tjp.jptype()) {
    fct << "  inline static AC::JPType jptype() { return JPTYPE; }" << endl;
  }
  
  if (tjp.argtype()) {
    fct << "  inline static AC::Type const argtype(unsigned i) {" << endl;
    if (arg_count > 0) {
      fct << "    static AC::Type const type[" << arg_count << "] = {";
      list<ACM_Type*> tlist;
      get_arg_types (*loc, tlist);
      unsigned i = 0;
      for (list<ACM_Type*>::iterator iter = tlist.begin(); iter != tlist.end();
           ++iter, ++i) {
        if (i > 0) fct << ", ";
        fct << "\"";
        Naming::mangle (fct, *iter);
        fct << "\"";
      }
      fct << "};" << endl;
      fct << "    return type[i];" << endl;
      fct << "  }" << endl;
    } else {
      fct << "    return \"\";" << endl;
      fct << "  }" << endl;
    }
  }
   
  // begin of dynamic data ----------------------------------------------------
  if( tjp.arg_needed( loc ) ) {
    if (!tjp.useAction () && arg_count > 0)
      data << "  void *_args[ARGS];" << endl;
    fct <<  "  inline " << ( tjp.forced_const_args() ? "const " : "" ) << "void * arg (int n) {return "
        << (arg_count > 0 ? "_args[n]" : "0") << ";}" << endl;
    fct <<  "  template <int I> typename Arg<I>::ReferredType *arg () {"
        << endl;
    fct <<  "    return (typename Arg<I>::ReferredType*)arg (I);" << endl;
    fct <<  "  }" << endl;
  }

  if( tjp.result_needed( loc ) ) {
    if (!tjp.useAction ())
      data << "  void *_result;" << endl;
    fct <<  "  inline Result *result() {return (Result*)_result;}" << endl;
  }

  if( tjp.target_needed( loc ) ) {
    if (!tjp.useAction ())
      data << "  Target *_target;" << endl;
    fct <<  "  inline Target *target() {return (Target*)_target;}" << endl;
  }

  // experimental feature:
  if (project_.backend_supports_cxx(2017) && (loc->type_val() & (JPT_Get | JPT_Set))) {
    fct << "  inline static constexpr Entity *variable() { return ";
    ACM_Variable *var = (loc->type_val() & JPT_Get) ?
      static_cast<ACM_Get*>(loc)->get_variable() : static_cast<ACM_Set*>(loc)->get_variable();
    if (static_cast<ACM_Name*>(var->get_parent())->get_name() == "::")
      fct << "&::" << var->get_name();
    else
      fct << "nullptr";
    fct << "; }" << endl;
  }
  // this feature is only available in Clang and if enabled on cmdline
  if( tjp.entity_needed( loc ) ) {
    if( !tjp.useAction() )
      data << "  Entity *_entity;" << endl;
    fct << "  inline Entity *entity() {return (Entity *)_entity;}" << endl;
  }

  // member pointers cant be represented as void * so they are not available in action
  if( tjp.memberptr_needed( loc ) ) {
    if( tjp.memberptr_defined( loc ) ) {
      data << "  MemberPtr _memberptr;" << endl;
      fct << "  inline MemberPtr memberptr() {return _memberptr;}" << endl;
    }
    else // return void *0 for non member entities
      fct << "  inline MemberPtr memberptr() {return 0;}" << endl;
  }
  if( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) ) {
    if( ! tjp.useAction() && tjp.array_defined( loc ) ) {
      if( tjp.array_needed( loc ) )
        data << "  Array *_array;" << endl;
      if( tjp.idx_needed( loc ) )
        data << "  void *_indices[DIMS];" << endl;
    }
    if( tjp.array_needed( loc ) )
      fct << "  inline Array *array() {return (Array*)" << ( tjp.array_defined( loc ) ? "_array" : "0" ) << ";}" << endl;
    if( tjp.idx_needed( loc ) ) {
      if( tjp.array_defined( loc ) ) {
        fct << "  template <unsigned int D> typename Dim<D>::Idx idx() {" << endl;
        fct << "    return *((typename Dim<D>::Idx*)_indices[D]);" << endl;
        fct << "  }" << endl;
      }
      else
        fct << "  template <unsigned int D> void idx() { return; }" << endl;
    }
  }

  if( tjp.that_needed( loc ) ) {
    if (!tjp.useAction ())
      data << "  That *_that;" << endl;
    fct <<  "  inline That *that() {return (That*)_that;}" << endl;
  }

  if (tjp.wrapper() || tjp.useAction ()) {
    if (!tjp.useAction ())
      data << "  void (*_wrapper)(AC::Action &);" << endl;
    fct <<  "  inline void (*wrapper ())(AC::Action &) {return _wrapper;}" << endl;
  }

  if( tjp.has_wormhole() )
    data << "  __WORMHOLE *__wormhole;" << endl;

  // terminate all the strings
  types << endl;
  data << endl;
  fct << endl;
  
  // add all types, attributes, and functions (on level 0)
  assert( depth == 0 ); // the functions returns early if not on level 0
  code << types.rdbuf();
  code << data.rdbuf();
  code << fct.rdbuf();
  
  // the closing bracket is *NOT* generated here -> for external extensions
}


ACM_Function *CodeGenerator::that_func (ACM_Code *loc) {
  ACM_Name *parent = get_explicit_parent( *loc );
  return ( parent && ( parent->type_val() == JPT_Function ) ) ? static_cast<ACM_Function *>( parent ) : 0;
}


// generate the initialization of the JoinPoint data structure for a joinpoint
void CodeGenerator::tjp_init (std::ostream &code, const ThisJoinPoint &tjp, ACM_Code *loc,
  int depth, bool is_dep, std::vector<std::string> *arg_names,
  int wrapper_number ) const {

  JoinPointType jptype = loc->type_val ();
  
  if (tjp.pointer_needed ()) {

    code << "  __TJP ";
    Naming::tjp_instance(code, loc);
    code << ";" << endl;

    TI_Builtin *builtin = 0;
    if( loc->type_val() == JPT_Builtin )
      builtin = TI_Builtin::of( static_cast<ACM_Builtin &>( *loc ) );

    int args = get_arg_count(*loc);
    if (tjp.arg_needed (loc)) {
      if (args) {
        if (tjp.useAction ()) {
          code << "  void *";
          Naming::tjp_args_array(code, loc);
          code << "[] = { ";
          for (int i = 0; i < args; i++) {
            if (i > 0)
              code << ", ";
            code << "(void*)";
            if( builtin && builtin->arg_is_unavailable( i, wrapper_number ) ) {
              code << "0";
              continue;
            }
            else
              code << "&";
            if (arg_names)
              code << (*arg_names)[i];
            else
              code << "arg" << i;
          }
          code << " };" << endl;
        }
      }

      if (tjp.useAction ()) {
        code << "  ";
        Naming::tjp_instance(code, loc);
        code << "._args = ";
        if (args)
          Naming::tjp_args_array(code, loc);
        else
          code << "0";
        code << ";" << endl;
      }
      else {
        for (int i = 0; i < args; i++) {
          code << "  ";
          Naming::tjp_instance(code, loc);
          code << "._args[" << i << "] = (void*)";
          if( builtin && builtin->arg_is_unavailable( i, wrapper_number ) ) {
            code << "0;" << endl;
            continue;
          }
          else
            code << "&";
          if (arg_names)
            code << (*arg_names)[i];
          else
            code << "arg" << i;
          code << ";" << endl;
        }
      }
    }
    
    if( tjp.result_needed( loc ) ) {
      // initialize _result: regardless of whether _result is inherited from AC::Action
      // or declared within the JoinPoint type it is a void pointer (void*).
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._result = ";
      if (!has_result_type(*loc) || get_result_type(*loc)->get_signature() == "void") {
        code << "0";
      } else {
        code << "(void*)&" << ACResultBuffer::result_name();
      }
      code << ";" << endl;
    }

    // this feature is only available in Clang and if enabled on cmdline
    if( tjp.entity_needed( loc ) ) {
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._entity = ";
      if( loc->type_val() & ( JPT_Code & ~ JPT_Builtin ) ) {
        code << " (";
        if (tjp.useAction ())
          code << "void*)";
        else
          code << (is_dep ? "typename " : "") << "__TJP::Entity *)";
        if( has_entity_func( *loc ) && needs_this( *get_entity_func( *loc ) ) )
          code << "0"; // for member function we can't aquire a normal function pointer
        else
          code << "&ent";
      }
      else {
        code << " 0";
      }
      code << ";" << endl;
    }

    // this feature is only available in Clang and if enabled on cmdline
    if( tjp.memberptr_defined( loc ) && tjp.memberptr_needed( loc ) ) {
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._memberptr = " << "(" << (is_dep ? "typename " : "") << "__TJP::MemberPtr)" << "member;" << endl;
    }

    if( loc->type_val() & ( JPT_Access & ~ ( JPT_Call | JPT_Builtin ) ) ) {
      if( tjp.array_needed( loc ) && tjp.array_defined( loc ) ) {
        code << "  ";
        Naming::tjp_instance( code, loc );
        code << "._array = (";
        if( tjp.useAction() )
          code << "void*)";
        else
          code << (is_dep ? "typename " : "") << "__TJP::Array*)";
        code << "&base;" << endl;
      }
      else if( tjp.useAction() ) { // useAction() => array_needed() , thus array_defined() == false here
        code << "  ";
        Naming::tjp_instance( code, loc );
        code << "._array = 0;" << endl;
      }

      const unsigned int idx_count = TI_Access::of( *static_cast<ACM_Access *>( loc ) )->entity_index_count();
      if( tjp.useAction() ) {
        code << "  void *__idx_array[] = { ";
        for( unsigned int i = 0; i < idx_count; i++ ) {
          if( i > 0 )
            code << ", ";
          code << "(void*)&idx" << i;
        }
        code << " };" << endl;

        code << "  ";
        Naming::tjp_instance( code, loc );
        code << "._indices = " << ( tjp.array_defined( loc ) ? "__idx_array" : "0" ) << ";" << endl;
      }
      else if( tjp.idx_needed( loc ) && tjp.array_defined( loc ) ) {
        for( unsigned int i = 0; i < idx_count; i++ ) {
          code << "  ";
          Naming::tjp_instance( code, loc );
          code << "._indices[" << i << "] = (void *)&idx" << i << ";" << endl;
        }
      }
    }
    
    if( tjp.target_needed( loc ) ) {
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._target = ";
      if( has_entity( *loc ) && needs_this( *get_entity( *loc ) ) ) {
        assert(jptype != JPT_Builtin);
        code << " (";
        if (tjp.useAction ())
          code << "void*)";
        else {
          code << (is_dep ? "typename " : "") << "__TJP::Target*)";
        }
        switch (jptype) {
        // inner joinpoints
        case JPT_Construction:
        case JPT_Destruction:
        case JPT_Execution:
          code << "this";
          break;
        // outer joinpoints
        case JPT_Call:
        case JPT_Get:
        case JPT_Set:
        case JPT_Ref:
          code << "&dst";
          break;
        default:
          code << " 0";
        }
      } else {
        code << " 0";
      }
      code << ";" << endl;
    }

    if( tjp.that_needed( loc ) ) {
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._that = ";
      if (that_func (loc) && needs_this (*that_func (loc))) {
        code << " (";
        if (tjp.useAction ())
          code << "void*)";
        else {
          code << (is_dep ? "typename " : "")<< "__TJP::That*)";
        }
        switch (jptype) {
        // inner joinpoints
        case JPT_Construction:
        case JPT_Destruction:
        case JPT_Execution:
          code << "this";
          break;
        // outer joinpoints
        case JPT_Call:
        case JPT_Builtin:
        case JPT_Get:
        case JPT_Set:
        case JPT_Ref:
        case JPT_GetRef:
        case JPT_SetRef:
          code << "srcthis";
          break;
        default:
            code << " 0";
        }
      } else {
        code << " 0";
      }
      code << ";" << endl;
    }
    
    if (tjp.useAction ()) {
      code << "  ";
      Naming::tjp_instance(code, loc);
      code << "._fptr = ";
      code << "0";
      code << ";" << endl;
    }
  }
}

void CodeGenerator::advice_invocation_func (ostream &out, const AdviceInfo &advice_info,
  bool def) const {

  ACM_AdviceCode &code = advice_info.code();
  ACM_Aspect &aspect = advice_info.aspect();
  AspectInfo &aspect_info = advice_info.aspect_info();
  clang::FunctionDecl *ad_func = TI_AdviceCode::of (code)->decl ();
  clang::NamedDecl *ad_cls = llvm::cast<clang::NamedDecl>(ad_func->getParent ());
  
  const ThisJoinPoint &tjp = TI_AdviceCode::of (code)->this_join_point ();
  const ThisJoinPoint &aspectof_tjp = aspect_info.aspectof_this_join_point ();
    
  // determine whether the invocation functions needs JoinPoint, tjp, or Bind.
  int first_context_arg = (TI_AdviceCode::of (code)->needs_joinpoint_obj() ? 1 : 0);
  bool has_context_var = TI_AdviceCode::of (code)->needs_context_vars();
  bool type_needed = tjp.type_advice_needed () || aspectof_tjp.type_advice_needed () ||
                     has_context_var;
  bool pointer_needed = tjp.pointer_advice_needed () || has_context_var ||
                        aspectof_tjp.pointer_advice_needed ();
    
  if (type_needed)
    out << "  template <class JoinPoint>" << endl;

  out << "  ";
  if (beps_._use_always_inline)
    out << "__attribute((always_inline)) ";

  ostringstream suffix;
  suffix << signature(aspect) << "_" << advice_info.scope_name () << advice_info.name ();
  out << "inline void invoke_" << suffix.str () << " (";

  if (pointer_needed)
    out << "JoinPoint *tjp";
  out << ")" << (def ? " {" : ";") << endl;

  // the declaration ends here
  if (!def)
    return;
      
  // generate typedefs
  // new implementation might have one argument (tjp)
  for (int a = first_context_arg; a < (int)ad_func->getNumParams (); a++)
//    out << "    typedef typename Binding::template Arg<" << a << "> Arg" << a 
//        << ";" << endl;
    out << "    typedef typename JoinPoint::Binding_" << suffix.str ()
        << "::template Arg<" << (a - first_context_arg) << "> Arg"
        << (a - first_context_arg) << ";" << endl;

  // generate advice call
  out << "    ";
  clang::Decl *aspect_cls = TI_Aspect::of (advice_info.aspect ())->decl();
  if (ad_cls != aspect_cls)
    out << "((::" << ad_cls->getNameAsString () << "*)";
  // aspectof function
  out << "::" << signature (advice_info.aspect ()) << "::aspectof";
  // generate <JoinPoint> if necessary
  if (aspectof_tjp.type_advice_needed () && !aspectof_tjp.pointer_advice_needed ())
    out << "<JoinPoint>";
  out << "(";
  if (aspectof_tjp.pointer_advice_needed ())
    out << "tjp";
  out << ")";
    
  if (ad_cls != aspect_cls)
    out << ")";
  out << "->";
  if (tjp.type_advice_needed () && !tjp.pointer_advice_needed())
    out << "template ";  
  out << advice_info.name ();

  // generate <JoinPoint> if necessary
  if (tjp.type_advice_needed () && !tjp.pointer_advice_needed())
    out << "<JoinPoint>";
  out << " (";

  int nargs = 0;

  if (tjp.pointer_advice_needed()) {
    out << "tjp";
    nargs++;
  }

  // new implementation might have one argument (tjp)
  for (int a = first_context_arg; a < (int)ad_func->getNumParams (); a++) {
    if (nargs > 0)
       out << ", ";
    out << "(" << TI_Type::get_type_text(ad_func->getParamDecl(a)->getType(),
                                           &ad_func->getASTContext(),
                                           0,
                                           TSEF_ENABLE,
                                           false,
                                           TSEF_DONOTCHANGE,
                                           false,
                                           true,
                                           false);
    out << ")";
    out << "Arg" << (a - first_context_arg) << "::val (tjp)";
    nargs++;
  }

  out << ");" << endl;

  out << "  }" << endl;
}

// generate the call of an advice invocation function
void CodeGenerator::advice_invocation_func_call (ostream &stmt,
  const AdviceInfo &advice_info, const string &tjp_tp, const string &tjp_obj) {

  ACM_AdviceCode &code = advice_info.code();
  ACM_Aspect &aspect = advice_info.aspect();
  AspectInfo &aspect_info = advice_info.aspect_info();

  const ThisJoinPoint &tjp = TI_AdviceCode::of (code)->this_join_point ();
  const ThisJoinPoint &aspectof_tjp = aspect_info.aspectof_this_join_point ();
    
  // determine whether the invocation functions needs JoinPoint or tjp
  // new implementation has at least one argument
  bool has_context = TI_AdviceCode::of (code)->needs_context_vars();

  bool type_needed = tjp.type_advice_needed () || aspectof_tjp.type_advice_needed () ||
                     has_context;
  bool pointer_needed = tjp.pointer_advice_needed () || has_context ||
                        aspectof_tjp.pointer_advice_needed ();
    
  stmt << "AC::invoke_" << signature (aspect) << "_"
       << advice_info.scope_name () << advice_info.name ();
       
  if (type_needed) {
    stmt << "<" << tjp_tp;
//    if (has_context) {
//      string tp (tjp_tp);
//      tp.erase (tp.length () - 2, tp.length ());
//      stmt << ", Binding_" << tp << "_0_" << _aspect->name () << "_"
//           << Scope ()->Name () << "_" << (name () + 1);
//    }
    stmt << ">";
  }

  stmt << " (";
  if (pointer_needed)
    stmt << tjp_obj;
  stmt << ");";
}

// generate a binding template for advice
void CodeGenerator::advice_binding_template (ostream &out,
  const AdviceInfo &advice_info, const std::string &jpname) const {

  ACM_AdviceCode &code = advice_info.code();
  ACM_Aspect &aspect = advice_info.aspect();

  // later this will be passed as an argument
  const Binding &bind = advice_info.binding ();

  // no code generation if no context variables are expected
  if (!bind.is_used())
    return;
      
  // generate the mapping of context variables to advice function arguments
  out << "struct Binding_" /* << jpname << "_" */ << signature(aspect) << "_"
      << advice_info.scope_name () << "_" << (advice_info.name ().c_str() + 1) << " {" << endl;
  out << "  typedef " /* << jpname*/ << "__TJP" << " TJP;" << endl;
  out << "  template <int I";
  if (beps_._spec_scope)
    out << ", int DUMMY = 0";
  out << "> struct Arg {" << endl;
  out << "    void val (TJP *tjp) {} // for VC7" << endl;
  out << "  };" << endl;

  typedef ACM_Container<ACM_Arg, true> Container;
  Container &arguments = code.get_pointcut()->get_args();
  int a = 0;
  for (Container::iterator i = arguments.begin (); i != arguments.end (); ++i, ++a) {
    // Buffer the current clang::QualType:
    const clang::QualType curr_type = TI_Arg::of(*(*i))->type();
    const bool is_pointer = curr_type->isPointerType ();
    // Determine whether the current type is a reference type:
    const bool is_reference = curr_type->isReferenceType();
    int bind_index = bind.find (*i);
    assert (bind_index != Binding::NOT_FOUND);
    out << "  template <";
    if (beps_._spec_scope)
      out << "int DUMMY";
    out << "> struct Arg<" << a;
    if (beps_._spec_scope)
      out << ", DUMMY";
    out << "> {" << endl
        << "    static typename TJP::";
    switch (bind_index) {
      case Binding::THAT:
        out << "That " << (is_pointer ? "*" : "&")
            << "val (TJP *tjp) { return " << (is_pointer ? "" : "*")
            << "tjp->that (); }" << endl;
        break;
      case Binding::TARGET:
        out << "Target " << (is_pointer ? "*" : "&")
            << "val (TJP *tjp) { return " << (is_pointer ? "" : "*")
            << "tjp->target (); }" << endl;
        break;
      case Binding::RESULT:
        out << "Res::ReferredType &val (TJP *tjp) { return " << (is_reference ? "*" : "") <<  "*tjp->result (); }" << endl;
        break;
      case Binding::NOT_FOUND: // already handled by assertion
        break;
      default: // argument
        out << "template Arg<" << bind_index << ">::ReferredType &val (TJP *tjp) {"
            << " return *tjp->template arg<" << bind_index << "> (); }" << endl;
    }
    out << "  };" << endl;
  }

  out << "};" << endl;  
}


void CodeGenerator::binding_templates (ostream &out, ACM_CodePlan *plan,
    const string &jpname) const {
  // later the individual bindings have to be passed from here, too.
  typedef ACM_Container<ACM_CodeAdvice, true> Container;
  Container &before = plan->get_before();
  for (Container::iterator i = before.begin (); i != before.end (); ++i)
    advice_binding_template (out, *TI_CodeAdvice::of(*(*i))->get_advice_info(), jpname);
  if (plan->has_around())
    advice_binding_template (out, *TI_CodeAdvice::of(*plan->get_around())->get_advice_info(), jpname);
  if (plan->has_next_level())
    binding_templates (out, plan->get_next_level(), jpname);
  Container &after = plan->get_after();
  for (Container::iterator i = after.begin (); i != after.end (); ++i)
    advice_binding_template (out, *TI_CodeAdvice::of(*(*i))->get_advice_info(), jpname);
}
