// 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                                            

// AspectC++ includes
#include "PointCutExpr.h"
#include "PointCutContext.h"
#include "Binding.h"
#include "Naming.h"
#include "MatchExpr.h"
#include "TrackerDog.h"

// PUMA includes
#include "Puma/ErrorStream.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CArgumentInfo.h"
#include "Puma/CClassInfo.h"
#include "Puma/CClassDatabase.h"
#include "Puma/CTree.h"
#include "Puma/Filter.h"

#include <assert.h>
#include <sstream>
using std::stringstream;
using std::endl;
using std::ends;

static CClassInfo *cscope(CObjectInfo *obj) {
  if (obj->QualifiedScope ())
    return obj->QualifiedScope ()->ClassInfo ();
  return obj->Scope ()->ClassInfo ();
}

void PointCutExpr::sem_args (ErrorStream &err, PointCutContext &context) {
  // recursively do a semantic analysis of child nodes
  for (int i = 0; i < args (); i++)
    if (arg (i))
      arg (i)->semantics (err, context);
}

void PointCutExpr::check_arg_types (ErrorStream &err, const char *func, 
				    PCE_Type expected) {
  // it is an error if something is provided, which was not expected
  for (int i = 0; i < args (); i++) {
    if (arg (i) && arg (i)->type () != expected) {
      err << sev_error << node ()->token ()->location ()
	  << "'" << func << "' expects a "
	  << (expected == PCE_NAME ? "name" : "code") << " pointcut"
	  << " as argument";
      if (args  () > 1)
	err << " " << (i + 1);
      err << endMessage;
    }
  }
}

void PointCutExpr::check_arg_types_equal (ErrorStream &err, const char *func) {
  if (!arg (0)) return;
  // it is an error if the types of all arguments are not equal
  PCE_Type first_type = arg (0)->type ();
  for (int i = 1; i < args (); i++) {
    if (arg (i) && arg (i)->type () != first_type) {
      err << sev_error << node ()->token ()->location ()
	  << "'" << func << "' expects that all argument types are equal"
	  << endMessage;
    }
  }
}

const CArgumentInfo *PointCutExpr::get_binding (const char *name,
						ErrorStream &err, 
						PointCutContext &context) {
  if (!context.func ()) {
    err << sev_error << node ()->token ()->location () 
	<< "context variable '" << name
	<< "' is not support in this kind of pointcut expression"
	<< endMessage;
    return 0;
  }
  ArgSet &curr_arg_bindings = *context.arg_bindings ().top ();
  CArgumentInfo *arginfo = (CArgumentInfo*)0;
  unsigned int a;
  for (a = 0; a < context.func ()->Arguments (); a++) {
    if (strcmp (context.func ()->Argument (a)->Name (), name) == 0) {
      arginfo = context.func ()->Argument (a);
      break;
    }
  }
  if (!arginfo) {
    err << sev_error << node ()->token ()->location () 
	<< "'" << name
	<< "' is not in argument list" << endMessage;
    return 0;
  }
  return curr_arg_bindings.lookup (a);
}

bool PCE_SingleArg::check_derived (CClassInfo *cls, CFunctionInfo *func,
				   PointCutContext &context, Binding &binding, Condition &cond) {
  Condition unused;
  bool do_subclasses = true;
  for (unsigned i = 0; i < cls->DerivedClasses (); i++) {
    CClassInfo *derived = cls->DerivedClass (i);
    if (func) {
      for (unsigned f = 0; f < derived->Functions (); f++) {
        if (!func->hasSameNameAndArgs (derived->Function (f)))
          continue;
        JoinPointLoc *func_loc = context.scopes ().lookup (
          TrackerDog::unique_object (derived->Function (f)));
        if (func_loc && arg (0)->evaluate (*func_loc, context, binding, unused))
	        return true;
      }
    }
    else {
      JoinPointLoc *class_loc = context.scopes ().lookup (derived);
      if (class_loc &&
          arg (0)->evaluate (*class_loc, context, binding, unused)) {
        if (context.in_that () || context.in_target ()) {
          cond.matches (derived);
          do_subclasses = false; // don't check subclasses, but siblings
        }
        else
          return true;
      }
    }
    if (do_subclasses && check_derived (derived, func, context, binding, cond))
      return true;
  }
  return !do_subclasses;
}

bool PCE_SingleArg::check_base (CClassInfo *cls, CFunctionInfo *func,
				PointCutContext &context, Binding &binding, Condition &cond) {
  if (func) {
    for (unsigned f = 0; f < cls->Functions (); f++) {
      if (!func->hasSameNameAndArgs (cls->Function (f)))
        continue;
      JoinPointLoc *func_loc = context.scopes ().lookup (
        TrackerDog::unique_object (cls->Function (f)));
      if (func_loc && arg (0)->evaluate (*func_loc, context, binding, cond))
  	    return true;
    }
  }
  else {
    JoinPointLoc *class_loc = context.scopes ().lookup (cls);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }
  for (unsigned i = 0; i < cls->BaseClasses (); i++) {
    if (check_base (cls->BaseClass (i)->Class (), func, context, binding, cond))
      return true;
  }
  return false;
}


PCE_Type PCE_Classes::type () const {
  return PCE_NAME;
}

void PCE_Classes::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "classes", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Classes::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                            Binding &binding, Condition &cond) {
  return jpl.type () == JoinPointLoc::Class &&
    arg (0)->evaluate (jpl, context, binding, cond);
}

PCE_Type PCE_Base::type () const {
  return PCE_NAME;
}

void PCE_Base::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "base", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Base::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                         Binding &binding, Condition &cond) {
  if (jpl.type () & (JoinPointLoc::Class | JoinPointLoc::Aspect)) {
    CClassInfo *cls = ((JPL_Class&)jpl).class_info ();
    // the class is a base of the argument class(es) if one of its derived
    // classes is described by the argument
    return check_derived (cls, 0, context, binding, cond);
  }
  else {
    CFunctionInfo *func = ((JPL_Function&)jpl).func_info ();
    CClassInfo *cls = cscope (func);
    return cls && check_derived (cls, func, context, binding, cond);
  }
}

PCE_Type PCE_Derived::type () const {
  return PCE_NAME;
}

void PCE_Derived::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "derived", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Derived::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                            Binding &binding, Condition &cond) {
  if (jpl.type () & (JoinPointLoc::Class | JoinPointLoc::Aspect)) {
    CClassInfo *cls = ((JPL_Class&)jpl).class_info ();
    // the class is derived of the argument class(es) if the class itself or
    // one of its base classes is described by the argument
    return check_base (cls, 0, context, binding, cond);
  }
  else {
    CFunctionInfo *func = ((JPL_Function&)jpl).func_info ();
    CClassInfo *cls = cscope (func);
    return cls && check_base (cls, func, context, binding, cond);
  }
}

PCE_Type PCE_Within::type () const {
  return PCE_CODE;
}

void PCE_Within::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "within", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Within::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                           Binding &binding, Condition &cond) {
  if (jpl.type () == JoinPointLoc::MethodCall &&
      ((JPL_MethodCall&)jpl).is_pseudo ()) {
    context.pseudo_true (true);
    return true;
  }

  // TODO: here I should better use the virtual that_type() function
  CFunctionInfo *func;
  switch (jpl.type ()) {
  case JoinPointLoc::Method:
    func = ((JPL_Method&)jpl).func_info (); break;
  case JoinPointLoc::Construction:
    func = ((JPL_Construction&)jpl).func_info (); break;
  case JoinPointLoc::Destruction:
    func = ((JPL_Destruction&)jpl).func_info (); break;
  case JoinPointLoc::MethodCall:
    func = ((JPL_MethodCall&)jpl).caller (); break;
  default:
    return false; // should not happen
  }
  
  if (jpl.type () != JoinPointLoc::Method &&
      jpl.type () != JoinPointLoc::Construction &&
      jpl.type () != JoinPointLoc::Destruction) {
    // only within("aClass") returns methods
    JoinPointLoc *func_loc = context.scopes ().lookup (func);
    if (func_loc && arg (0)->evaluate (*func_loc, context, binding, cond))
      return true;
  }

  CClassInfo *cls = cscope (func);
  if (cls) {
    // only member functions are relevant for within("aClass")
    JoinPointLoc *class_loc = context.scopes ().lookup (cls);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }
  return false;
}

PCE_Type PCE_Execution::type () const {
  return PCE_CODE;
}

void PCE_Execution::semantics (ErrorStream &err,
			       PointCutContext &context) {
  check_arg_types (err, "execution", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Execution::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                              Binding &binding, Condition &cond) {
  // consider execution join point only
  if (jpl.type () != JoinPointLoc::Method)
    return false;

  // get the class and function object
  CFunctionInfo *that_func = ((JPL_Method&)jpl).func_info ();
  CClassInfo *that_class = cscope (that_func);

  // if the function is not part of the project it is not considered to
  // be an execution join point
  if (!context.in_project (that_func))
    return false;

  if (that_class) {
    // check if the class, which is the scope of the current exec join point
    // is described by the argument
    JoinPointLoc *class_loc = context.scopes ().lookup (that_class);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }

  // check if the exec join point's function name is described by the argument
  JoinPointLoc *func_loc = context.scopes ().lookup (that_func);
  if (func_loc && arg (0)->evaluate (*func_loc, context, binding, cond))
    return true;

  // no match!
  return false;
}

PCE_Type PCE_Call::type () const {
  return PCE_CODE;
}

void PCE_Call::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "call", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Call::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                         Binding &binding, Condition &cond) {
  if (jpl.type () != JoinPointLoc::MethodCall)
    return false;
    
  CFunctionInfo *target_func = ((JPL_MethodCall&)jpl).called ();
  CClassInfo *target_class = cscope (target_func);

  if (target_class) {
    // check if the target class is described by the argument
    JoinPointLoc *class_loc = context.scopes ().lookup (target_class);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }

  // check if the target function name is described by the argument
  JoinPointLoc *func_loc = context.scopes ().lookup (target_func);
  if (func_loc && arg (0)->evaluate (*func_loc, context, binding, cond))
    return true;

  // no match!
  return false;
}


PCE_Type PCE_Construction::type () const {
  return PCE_CODE;
}

void PCE_Construction::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "construction", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Construction::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                                 Binding &binding, Condition &cond) {
  if (jpl.type () != JoinPointLoc::Construction)
    return false;
  CFunctionInfo *func = ((JPL_Construction&)jpl).func_info ();
  CClassInfo *cls = cscope (func);
  if (cls) { // TODO: this is because unions yield 0 here. Handle them!
    JoinPointLoc *class_loc = context.scopes ().lookup (cls);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }
  return false;
}

PCE_Type PCE_Destruction::type () const {
  return PCE_CODE;
}

void PCE_Destruction::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "destruction", PCE_NAME);
  sem_args (err, context);
}

bool PCE_Destruction::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                                Binding &binding, Condition &cond) {
  if (jpl.type () != JoinPointLoc::Destruction)
    return false;
  CFunctionInfo *func = ((JPL_Destruction&)jpl).func_info ();
  CClassInfo *cls = cscope (func);
  if (cls) { // TODO: this is because unions yield 0 here. Handle them!
    JoinPointLoc *class_loc = context.scopes ().lookup (cls);
    if (class_loc && arg (0)->evaluate (*class_loc, context, binding, cond))
      return true;
  }
  return false;
}


PCE_Type PCE_That::type () const {
  return PCE_CODE;
}

void PCE_That::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "that", PCE_NAME);
  context.enter_that ();
  sem_args (err, context);
  context.leave_that ();
}

bool PCE_That::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                         Binding &binding, Condition &cond) {
  if (jpl.type () == JoinPointLoc::MethodCall &&
      ((JPL_MethodCall&)jpl).is_pseudo ()) {
    context.pseudo_true (true);
    return true;
  }
    
  // TODO: use a virtual JoinPointLoc method instead of this...
  CFunctionInfo *that_func = 0;
  if (jpl.type () == JoinPointLoc::Method)
    that_func = ((JPL_Method&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::Construction)
    that_func = ((JPL_Construction&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::Destruction)
    that_func = ((JPL_Destruction&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::MethodCall)
    that_func = ((JPL_MethodCall&)jpl).caller ();
  else
    return false;

  CClassInfo *that_class = cscope (that_func);

  // if the function is not part of the project, this is no real join point
  if (!context.in_project (that_func))
    return false;
    
  // if this is not a member function return false
  if (!that_class || that_func->isStaticMethod ())
    return false;
  
  context.enter_that ();
  binding._this = (CArgumentInfo *)0;

  // if any of the class' base classes matches the argument the current class
  // is definitely an object of the required type
  if (!check_base (that_class, 0, context, binding, cond)) {
    // in a construction or destruction the object has exactly the type
    // of the constructor/destructor scope => no run-time check
    if (jpl.type () == JoinPointLoc::Construction ||
        jpl.type () == JoinPointLoc::Destruction) {
      context.leave_that ();
      return false;
    }
      
    // create a 'that' condition with a mangled name
    stringstream mangled, cond_name;
    arg (0)->mangle_type_check (mangled);
    mangled << ends;
    Naming::type_check_func (cond_name, that_class, mangled.str ().data ());
    cond_name << ends;
//    cout << "cond name: " << cond_name.str ().data () << endl;
    cond.that (cond_name.str ().data ());

    // find the derived classes that match the argument
    check_derived (that_class, 0, context, binding, cond);
  }
  context.leave_that ();
  return true;
}

PCE_Type PCE_Target::type () const {
  return PCE_CODE;
}

void PCE_Target::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "target", PCE_NAME);
  context.enter_target ();
  sem_args (err, context);
  context.leave_target ();
}

bool PCE_Target::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                           Binding &binding, Condition &cond) {
  // only calls have a target at the moment
  if (jpl.type () != JoinPointLoc::MethodCall)
    return false;
    
  JPL_MethodCall &jpl_call = (JPL_MethodCall&)jpl;
  CFunctionInfo *source_func = jpl_call.is_pseudo () ? 0 : jpl_call.caller ();
  CFunctionInfo *target_func = ((JPL_MethodCall&)jpl).called ();
  CClassInfo *target_class = cscope (target_func);

  // if the caller function is not part of the project, the call is not
  // considered to be a call join point
  if (source_func && !context.in_project (source_func))
    return false;
  
  // if this is not a member function return false
  if (!target_class || target_func->isStaticMethod ())
    return false;
  
  context.enter_target ();
  binding._target = (CArgumentInfo *)0;
  // if any of the class' base classes matches the argument the current class
  // is definitely an object of the required type
  if (!check_base (target_class, 0, context, binding, cond)) {
    // create a 'that' condition with a mangled name
    stringstream mangled, cond_name;
    arg (0)->mangle_type_check (mangled);
    mangled << ends;
    Naming::type_check_func (cond_name, target_class, mangled.str ().data ());
    cond_name << ends;
    cond.target (cond_name.str ().data ());

    // find the derived classes that match the argument
    check_derived (target_class, 0, context, binding, cond);
  }
  context.leave_target ();
  return true;
}

PCE_Type PCE_CFlow::type () const {
  return PCE_CODE;
}

void PCE_CFlow::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "cflow", PCE_CODE);
  sem_args (err, context);
  // evaluate the argument pointcut
  JoinPointLocList::Selection all;
  context.world ().select (JoinPointLoc::Code, all);
  for (JoinPointLocList::Selection::iterator iter = all.begin ();
       iter != all.end (); ++iter) {
    JoinPointLoc &jpl = **iter;
    Binding cflow_binding;
    Condition cflow_condition;
    context.pseudo_true (false); // todo sichern!
    if (arg (0)->evaluate (jpl, context, cflow_binding, cflow_condition)) {
      if (cflow_binding._used) {
        err << sev_error 
            << node ()->token ()->location ()
            << "context variables not supported in cflows" << endMessage;
        break;
      }
      if (cflow_condition) {
        err << sev_error 
            << node ()->token ()->location ()
            << "runtime conditions not supported in cflows" << endMessage;
        break;
      }
      _arg_pointcut.append (*new JoinPoint (&jpl, cflow_condition));
    }
  }
  _index = context.cflow (this); // remember this node in the context
}

bool PCE_CFlow::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                          Binding &binding, Condition &cond) {
  // check if this join point is one of the cflow starting points
  if (_arg_pointcut.find (&jpl) != _arg_pointcut.end ())
    return true; // no runtime condition check!
    
  // every other joinpoint might be in the cflow (space for improvement)
  cond.cflow (_index);
  return true;
}

PCE_Type PCE_Args::type () const {
  return PCE_CODE;
}

void PCE_Args::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "args", PCE_NAME);

  // recursively do a semantic analysis of child nodes
  int saved_arg = context.get_arg ();
  for (int i = 0; i < args (); i++) {
    context.set_arg (i);
    if (arg (i))
      arg (i)->semantics (err, context);
  }
  context.set_arg (saved_arg);
}

bool PCE_Args::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                         Binding &binding, Condition &cond) {
  CFunctionInfo *func = 0;
  if (jpl.type () == JoinPointLoc::Method)
    func = ((JPL_Method&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::Construction)
    func = ((JPL_Construction&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::Destruction)
    func = ((JPL_Destruction&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::MethodCall)
    func = ((JPL_MethodCall&)jpl).called ();
  else
    return false; // other join points are not considered, yet.
  
  JPL_Code *jpl_code = (JPL_Code*)&jpl;
    
  // check if the number of arguments is ok -> not compatible ("...")
  if (args () != jpl_code->arg_count ())
    return false;

  // check if all argument types match
  int saved_arg = context.get_arg ();
  for (unsigned a = 0; a < (unsigned)jpl_code->arg_count (); a++) {
    context.set_arg (a);
    binding._args[a] = (CArgumentInfo*)0;
    JPL_Type &type_loc = (JPL_Type&)jpl_code->arg_type ((int)a);
    if (!arg (a)->evaluate (type_loc, context, binding, cond)) {
      context.set_arg (saved_arg);
      return false;
    }
  }
  context.set_arg (saved_arg);

  return true;
}

PCE_Type PCE_Result::type () const {
  return PCE_CODE;
}

void PCE_Result::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "result", PCE_NAME);
  context.enter_result ();
  sem_args (err, context);
  context.leave_result ();
}

bool PCE_Result::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                           Binding &binding, Condition &cond) {
  CFunctionInfo *func = 0;
  if (jpl.type () == JoinPointLoc::Method)
    func = ((JPL_Method&)jpl).func_info ();
  else if (jpl.type () == JoinPointLoc::MethodCall)
    func = ((JPL_MethodCall&)jpl).called ();
  else
    return false; // other join points are not considered, yet.
  
  JPL_Code *jpl_code = (JPL_Code*)&jpl;
  JPL_Type &type_loc = (JPL_Type&)jpl_code->result_type ();

  context.enter_result ();
  binding._result = (CArgumentInfo *)0;
  bool result = arg (0)->evaluate (type_loc, context, binding, cond);
  context.leave_result ();
  return result;
}


PCE_Type PCE_Or::type () const {
  return arg (0)->type ();
}

void PCE_Or::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types_equal (err, "||");
  sem_args (err, context);
}

bool PCE_Or::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                       Binding &binding, Condition &cond) {
  Condition subcond0, subcond1;
  bool subresult0, subresult1;
  subresult0 = arg (0)->evaluate (jpl, context, binding, subcond0);
  subresult1 = arg (1)->evaluate (jpl, context, binding, subcond1);
  // if both subresults are false the disjunction is false, too
  if (!(subresult0 || subresult1))
    return false;
    
  // if any of the subresults was an unconditional true the result is true
  if ((subresult0 && !subcond0) || (subresult1 && !subcond1))
    return true;
    
  // at least one subresult was true, now we consider possible conditions
  if (subcond0) {
    cond.assign (subcond0);
    if (subcond1)
      cond.op_or (subcond1);
  }
  else if (subcond1) {
    cond.assign (subcond1);
  }
  return true;
}

PCE_Type PCE_And::type () const {
  return arg (0)->type ();
}

void PCE_And::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types_equal (err, "&&");
  sem_args (err, context);
}

bool PCE_And::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                        Binding &binding, Condition &cond) {
  Condition subcond0, subcond1;
  bool subresult0, subresult1;
  subresult0 = arg (0)->evaluate (jpl, context, binding, subcond0);
  bool pseudo_true0 = context.is_pseudo_true ();
  context.pseudo_true (false);
  subresult1 = arg (1)->evaluate (jpl, context, binding, subcond1);
  bool pseudo_true1 = context.is_pseudo_true ();
  context.pseudo_true (pseudo_true0 && pseudo_true1);
  
  // if any subresult was false the conjunction is false, too
  if (!(subresult0 && subresult1))
    return false;
  // both subresult were true, now we consider possible conditions
  if (subcond0) {
    cond.assign (subcond0);
    if (subcond1)
      cond.op_and (subcond1);
  }
  else if (subcond1) {
    cond.assign (subcond1);
  }
  return true;
}

PCE_Type PCE_Not::type () const {
  return arg (0)->type ();
}

void PCE_Not::semantics (ErrorStream &err, PointCutContext &context) {
  check_arg_types (err, "!", type ());
  sem_args (err, context);
}

bool PCE_Not::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                        Binding &binding, Condition &cond) {
  Condition subcond;
  bool subresult = arg (0)->evaluate (jpl, context, binding, subcond);
  // if the argument was true because we evaluate a pseudo call return true
  if (context.is_pseudo_true ())
    return true;
  // if the subexpression evaluates to an unconditional 'true' or false the
  // result must be inverted
  if (!subcond)
    return !subresult;
  // otherwise the result is 'true' but the condition must be negated
  else {
    cond.assign (subcond);
    cond.op_not ();
    return true;
  }
}

PCE_Type PCE_Named::type () const {
  return arg (0)->type ();
}

void PCE_Named::semantics (ErrorStream &err, PointCutContext &context) {
  ArgSet new_arg_bindings; // must be defined here and not in a local scope

  check_arg_types (err, "pointcut", type ());
  
  if (!node ()) {
    // root node of an expression, no call
    for (unsigned a = 0; a < context.func ()->Arguments (); a++)
      new_arg_bindings.append (context.func ()->Argument (a));
  }
  else {
    CT_ExprList *args = ((CT_CallExpr*)node ())->Arguments ();
    for (int s = 0; s < args->Entries (); s++) {
      CTree *arg = args->Entry (s);

      // check if the current argument node type is not a SimpleName
      if (arg->NodeName () != CT_SimpleName::NodeId ()) {
	err << sev_error << node ()->token ()->location ()
	    << "pointcut argument " << s << " must be an identifier" 
	    << endMessage;
	new_arg_bindings.append (0);
	continue;
      }

      // Argument must be a formal, lookup in the class database
      const char *name = ((CT_SimpleName*)arg)->Object ()->Name ();
      // get the argument binding
      const CArgumentInfo *arginfo = get_binding (name, err, context);;
      // .. and add it to the next binding context
      new_arg_bindings.append (arginfo);
    }
  }

  // set the current function pointer for context var lookups
  CFunctionInfo *saved_func = context.func (_func);

  // push the argument binding
  context.arg_bindings ().push (&new_arg_bindings);

  // analyze referenced expression tree
  sem_args (err, context);

  // pop the argument binding from the stack again
  context.arg_bindings ().pop ();

  context.func (saved_func); // restore old function pointer
}

bool PCE_Named::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                          Binding &binding, Condition &cond) {
  return arg (0)->evaluate (jpl, context, binding, cond);
}

PCE_Type PCE_ContextVar::type () const {
  return PCE_NAME;
}

void PCE_ContextVar::semantics (ErrorStream &err, 
				PointCutContext &context) {
  if (!(_bound_arg  = get_binding (_name, err, context)))
    return;
  _check_type = _bound_arg->TypeInfo ();

  if (context.in_that () || context.in_target ()) {
    if (_check_type->isPointer () && _check_type->BaseType ()->is_void ()) {
      // "void*" matches any class!
      _check_type = 0;
    }
    else {
      if (_check_type->isPointer () || _check_type->isAddress ())
      	_check_type = _check_type->BaseType ();
      if (!_check_type->isClass ()) {
        err << sev_error << node ()->token ()->location ()
            << "argument of 'that' or 'target' must be a class, pointer to "
            << "class, or reference to class" << endMessage;
      }
    }
    _bound_to = context.in_that () ? CV_THAT : CV_TARGET;
  }
  else if (context.in_result ()) {
    _bound_to = CV_RESULT;
  }
  else {
    _bound_to = CV_ARG;
    _arg = context.get_arg ();
  }
}

bool PCE_ContextVar::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                               Binding &binding, Condition &cond) {

  assert (((_bound_to == CV_ARG || _bound_to == CV_RESULT) &&
           jpl.type () == JoinPointLoc::Type) ||
	        ((_bound_to == CV_TARGET || _bound_to == CV_THAT) && 
	         (jpl.type () & (JoinPointLoc::Class | JoinPointLoc::Aspect))));

  // determine the current type
  CTypeInfo *curr_type = 0;
  if (jpl.type () == JoinPointLoc::Type)
    curr_type = ((JPL_Type&)jpl).type_info ();
  else if (jpl.type () == JoinPointLoc::Class ||
           jpl.type () == JoinPointLoc::Aspect)
    curr_type = ((JPL_Class&)jpl).class_info ()->TypeInfo ();

  assert (curr_type);
  if (!curr_type)
    return false;
    
  // check if the current type matches
  if (_check_type && *_check_type != *curr_type)
    return false;

  // do the binding
  binding._used = true;
  switch (_bound_to) {
  case CV_THAT:
    binding._this = (CArgumentInfo*)_bound_arg;
    break;
  case CV_TARGET:
    binding._target = (CArgumentInfo*)_bound_arg;
    break;
  case CV_RESULT:
    binding._result = (CArgumentInfo*)_bound_arg;
    break;
  case CV_ARG:
    binding._args[_arg] = (CArgumentInfo*)_bound_arg;
    break;
  }
  return true;
}

void PCE_ContextVar::mangle_type_check (ostream &out) {
  _check_type->Mangled (out);
}


PCE_Type PCE_Match::type () const {
  return PCE_NAME;
}

void PCE_Match::semantics (ErrorStream &err, PointCutContext &context) {
  _match_expr.parse (err, node ()->token ()->location (), _dstr);
}

bool PCE_Match::evaluate (JoinPointLoc &jpl, PointCutContext &context,
                          Binding &binding, Condition &cond) {
  assert ((jpl.type () & JoinPointLoc::Name) != 0);
  assert (!_match_expr.error ());
  
  JPL_Name &jpl_name = (JPL_Name&)jpl;
  if ((jpl.type () == JoinPointLoc::Function && _match_expr.is_function ()) ||
      ((jpl.type () & (JoinPointLoc::Class | JoinPointLoc::Aspect |
        JoinPointLoc::Type)) && _match_expr.is_type ()))
    return _match_expr.matches (jpl_name.type_info (), jpl_name.obj_info ());
    
  return false;
//  bool old_result;        
//  if ((_filter.method () && jpl.type () != JoinPointLoc::Function) ||
//      (!_filter.method () && jpl.type () == JoinPointLoc::Function))
//    old_result = false;
//  else {          
//    old_result = ((JPL_Name&)jpl).matches (_dstr, _filter);
//    
////    if (old_result != new_result) {
////      cout << (old_result == new_result ? "same" : "diff ") 
////           << old_result << " " << new_result << " " << _dstr
////           << ": " << jpl.signature () << endl;
////    }
//  }
//  return old_result;
}

void PCE_Match::mangle_type_check (ostream &out) {
  const char *str = _dstr;
  while (*str) {
    switch (*str) {
      case ':' : out << "___S___"; break;
      case '%' : out << "___A___"; break;
      case '(' : out << "___L___"; break;
      case ')' : out << "___R___"; break;
      case ',': out << "___C___"; break;
      default: out << *str;
    }
    str++;
  }
}

