// 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 "Binding.h"
#include "ACBase/StringUtils.h"
#include <assert.h>

void Binding::reset () {
  _used = false;
  _problem_sc_arg_no_match = false;
  _problem_sc_arg_used = false;
  clear();
  resize(3, nullptr); // fill that, target, and result with 0
}

void Binding::bind(int what, ACM_Arg *val) {
  unsigned pos = what + _fixed;
  if (pos >= size())
    resize(pos + 1, nullptr);
  (*this)[pos] = val;
  _used = true;
}

bool Binding::has(int what) const {
  unsigned pos = what + _fixed;
  return size() > pos && (*this)[pos] != nullptr;
}

ACM_Arg *Binding::get(int what) const {
  unsigned pos = what + _fixed;
  if (pos > size())
    return nullptr;
  return (*this)[pos];
}

bool Binding::is_compatible_with (const Binding &other) const {
  if (!_used && !other._used)
    return true;
  if (size() != other.size())
    return false;
  for (unsigned int i = 0; i < size(); i++)
    if ((*this)[i] != other[i])
      return false;
  return true;
}

int Binding::find (ACM_Arg *arg) const {
  if (_used) {
    for (unsigned int a = 0; a < size (); a++) {
      if (arg == (*this)[a])
        return a - _fixed;
    }
  }
  return NOT_FOUND;
}

// analyze short-circuit argument problems for a given operator,
// set flags accordingly, and return the following number:
// For built-in operators with short-circuit evavluation (||, &&, ?:)
// return the number of the potentially unevaluated argument (1 or 2);
// 0 means that there is not short-circuit argument, i.e. no problem
// -1 means that the joinpoint cannot be matched.
int Binding::short_circuit_arg(const std::string &operator_name) {
  int needed_arg = 0; // default: match with no condition
  // remove "operator " (9 chars) from the target function name.
  assert(ACBase::starts_with(operator_name, "operator "));
  std::string op = operator_name.substr(9);
  bool is_ternary_expr = (op == "?:");
  bool is_short_circuiting = (is_ternary_expr || op == "||" || op == "&&");
  // Check whether this is no pseudo-call-join-point and a short-circuiting-join-point:
  if(is_short_circuiting) {
    // On join-points with short-circuit evaluation it is not guaranteed
    // that the second (or third in case of "?:"-operator) argument is available. Because the
    // decision about the evaluation of the second (or third) argument
    // comes at runtime, we first determine the short-circuit argument, that is bound by the
    // current args pointcut function, and afterwards we create a condition that compares the
    // available short-circuit argument with the needed argument at runtime.

    if(is_ternary_expr) {
      // Operator "?:"
      if(get(Binding::ARG1) != 0 && get(Binding::ARG2) != 0) {
        // Both short-circuiting arguments are bound, but it is not possible that
        // both short-circuiting arguments are available at the same time. We need
        // no runtime check and generate a warning if wanted. The warning will be
        // created only once per args pointcut function.
#if 0
        if(context.config().warn_limitations() && !both_sc_arg_used) {
          context.messages().push_back(pair<ErrorSeverity, string>(
              sev_warning, string("args pointcut function: join points at calls to the "
              "short-circuiting operator '?:' do not match (reason: both short-circuit "
              "arguments were bound, but they are never available at the same time).")));
          both_sc_arg_used = true;
        }
#endif
        set_problem_sc_arg_no_match();
        needed_arg = -1; // no match, no condition
      }
      else if(get(Binding::ARG1) == 0 && get(Binding::ARG2) == 0) {
        // Both short-circuiting arguments are *not* bound: We do not need a runtime check
        needed_arg = 0; // match, no condition
      }
      else {
        // Otherwise determine the needed/bound argument:
        needed_arg = ((get(Binding::ARG1) != 0) ? 1 : 2);
      }
    }
    else {
      // Operator "&&" or "||": Check for first argument:
      if(get(Binding::ARG1) != 0) {
        needed_arg = 1;
      }
    }

    // Check if an short-circuit argument was bound and we therefore need an argument at
    // runtime:
    if(needed_arg >= 1) {
      // If wanted we create a warning. This warning will be created only once per
      // args pointcut function.
#if 0
      if(context.config().warn_limitations() && !sc_arg_used) {
        context.messages().push_back(pair<ErrorSeverity, string>(
        sev_warning, string("args pointcut function: matching of join points at calls "
        "to short-circuiting operators '&&', '||' and '?:' is runtime dependent "
        "(reason: a short-circuit argument is bound by the args pointcut function and "
        "a match only occurs if the bound argument is available at runtime).")));
        sc_arg_used = true;
      }
#endif
      set_problem_sc_arg_used();
    }
  }
  return needed_arg;
}
