// see License.txt for copyright and terms of use

#include "garburator.h"        // this module
#include "garburator_cmd.h"    // GarburatorCmd
#include "garburator_global.h"
#include "oink.gr.gen.h"        // CCParse_Oink
#include "strutil.h"            // quoted
#include "oink_util.h"
#include "expr_visitor.h"
#include "squash_util.h"
#include "patcher.h"
#include "garburator_cmd.h"
#include "garburator_blacklister.h"
#include <set>
#include <sstream>
#include "squash_util.h"

typedef std::list<Variable*> var_list;
/* Can check function parameters, [global|class] variables, 
 * function return values */
class Garburator : public ExpressionVisitor {
public:
  Garburator(GarburatorCmd const &cmd, Patcher &patcher, Blacklister &blacklister): 
    cmd(cmd),
    patcher(patcher),
    inFunction(false),
    inDecl(false),
    str_nsCOMPtr(blacklister.str_nsCOMPtr),
    str_already_AddRefed(globalStrTable("already_AddRefed")),
    str_getter_AddRefs(blacklister.str_getter_AddRefs),
    blacklister(blacklister),
    str_address_of(blacklister.str_address_of),
    str_swap(blacklister.str_swap),
    str_nsGetterAddRefs(globalStrTable("nsGetterAddRefs"))
  {
  }

  virtual bool visitFunction(Function *f);
  virtual void postvisitFunction(Function *f);
  virtual void postvisitE_funCall(E_funCall *e);
  virtual bool visitE_funCall(E_funCall *e);
  virtual bool visitTypeSpecifier(TypeSpecifier *ts);
  virtual void postvisitTypeSpecifier(TypeSpecifier *ts);
  virtual bool visitDeclarator(Declarator *d);
  virtual bool visitE_constructor(E_constructor *c);
  virtual bool visitDeclaration(Declaration *d);
  virtual void postvisitDeclaration(Declaration *d);
  virtual bool visitS_decl(S_decl *s);
  virtual void postvisitE_constructor(E_constructor *e);
  virtual bool visitE_cast(E_cast *e);
  virtual bool visitE_keywordCast(E_keywordCast *e);
private:
  void remove_TemplateWrapper(PQ_template *pq, bool bracket);
  bool sourceMatches(UnboxedLoc const &loc, std::string const &file,
		     std::string const &word);

  bool rewriteTypeSpecifier(TypeSpecifier *ts, bool bracket = false);
  void rewrite_getter_AddRefs(E_funCall *e);
  void rewrite_getter_AddRefs(Expression *fcallCtor, Expression *arg,
			      bool stackOnly);
  bool visitD_func(D_func *df);

  void addCasts(E_funCall *e);
  void addConversions(E_funCall *e);
  void insertInitializers(var_list::iterator begin, 
                          var_list::iterator end,
                          CPPSourceLoc const &csl,
                          // use : instead of ,?
                          bool firstInitializer,
                          bool appendComma) 
    throw (x_match);

  bool onStack() {
    return inFunction || inDecl;
  }

  bool isStackVar(Variable *v);

  enum COMPTR_TYPE {
    COMPTR_TYPE_NONE = 0,
    COMPTR_TYPE_COMPTR,
    COMPTR_TYPE_ALREADY_ADDREFED
  };
  COMPTR_TYPE isCOMPtr(TypeSpecifier *ts);
  COMPTR_TYPE isCOMPtr(Type *t);

  GarburatorCmd const &cmd;
  Patcher &patcher;
  bool inFunction;
  bool inDecl;
  const StringRef str_nsCOMPtr;
  const StringRef str_already_AddRefed;
  typedef std::set<Variable*> var_set;
  var_set localSet;
  var_set stackClassMemberSet;
  const StringRef str_getter_AddRefs;
  Blacklister &blacklister;
  const StringRef str_address_of;
  const StringRef str_swap;
  const StringRef str_nsGetterAddRefs;
  std::list<TS_classSpec*> stackClassStack;
};

void GarburatorProcessor::process(GarburatorCmd const &cmd) {
  Patcher patcher;
  Blacklister blacklister;
  Garburator prGatherer(cmd, patcher, blacklister);
  foreachSourceFile {
    File *file = files.data();
    patcher.setFile(file->name);
    
    maybeSetInputLangFromSuffix(file);
    TranslationUnit *unit = file2unit.get(file);
    unit->traverse(blacklister);
    unit->traverse(prGatherer.loweredVisitor);
  }
}

bool Garburator::isStackVar(Variable *v) {
  return localSet.find(v) != localSet.end() 
    || stackClassMemberSet.find(v) != stackClassMemberSet.end();
}

/* used to check if we are looking at template instatiation or real code 
* I can't find a way to do that correctly in elsa 
* loc is one character behind word...yes it's a hack cos i'm lazy atm*/
bool Garburator::sourceMatches(UnboxedLoc const &loc, std::string const &file,
			       std::string const &word) {
  // verify that what we are looking at isn't a template inst
  std::string line = patcher.getLine(loc.line, file);
  std::string::size_type col = loc.col - word.size() - 1 - 1;
  line.erase(0, col);
  if (line.size() < word.size()) return false;
  line.erase(word.size());
  return line == word;
}

/*removes nsCOMPtr<> and already_AddRefed<>*/
void Garburator::remove_TemplateWrapper(PQ_template *pq, bool bracket) {
  CPPSourceLoc sloc(pq->loc);
  if (!sloc.hasExactPosition()) {
    cerr << toString(sloc.loc()) << ": Can't remove template wrapper" << endl;
    return;
  }

  UnboxedPairLoc pairLoc(PairLoc(sloc, sloc));
  STemplateArgument *arg = pq->sargs.first();
  CompoundType *ct = arg->getType()->asCompoundType();
  std::string name = ct->instName;//syntax->name->asPQ_name()->name;
  std::string::size_type offset = strlen(pq->name);
  std::string const &line = patcher.getLine(pairLoc.first.line, pairLoc.file);
  while(pairLoc.first.col + offset < line.size() 
	  && isspace(line[pairLoc.first.col + offset - 1])) {
    offset++;
  }

  pairLoc.second.col += offset + 1 + name.size() + 1;
  
  if (!sourceMatches(pairLoc.second, pairLoc.file, name)) return;
  
  // replace the "nsComPtr|already_AddRefed<"name">"
  std::stringstream ss;
  if (bracket)
    ss << "(";
  ss << name << "*";
  if (bracket)
    ss << ")";
  patcher.printPatch(ss.str(), pairLoc);
}

bool Garburator::visitTypeSpecifier(TypeSpecifier *ts) {
  // detect class annotated as stack-only
  try {
    static const StringRef str_stack = globalStrTable("\"NS_stack\"");
    static const StringRef str_user = globalStrTable("user");
    TS_classSpec *c = ts->asTS_classSpec();
    bool isStackClass = false;
    for (AttributeSpecifierList *asl = c->alist; asl; asl = asl->next) 
      try {
        AT_func *attrFunc = asl->spec->attr->asAT_func();
        xmatch(attrFunc->f == str_user);
        ArgExpression *e = SOME(attrFunc->args->first());
        xmatch(e->expr->asE_stringLit()->text == str_stack);
        isStackClass = true;
        break;
      } catch (x_match &) {
      }
    xmatch(isStackClass);
    stackClassStack.push_back(c);
    Restorer<bool> r(inDecl, true);
    // Mark+rewrite all of the comptr class members.
    // These remain there until garburator terminates
    // because it's hard to tell when the class goes out
    // of scope.
    for (ASTListIterNC<Member> i(c->members->list);
         ! i.isDone();
         i.adv()) {
      MR_decl *d = i.data()->ifMR_decl();
      if (!(d && isCOMPtr(d->d->spec))) continue;
  
      for (FakeList<Declarator>* decl = d->d->decllist;
       ! decl->isEmpty();
       decl = decl->butFirst()) {
        Variable *v = decl->first()->var;
        if (v->type->isPtrOrRef()) continue;
        // mark
        if (!v->type->isFunctionType())
          stackClassMemberSet.insert(v);
        //rewrite
        rewriteTypeSpecifier(d->d->spec);
      }
      
    }
    return true;
  } catch (x_match &) {
  }
  return !rewriteTypeSpecifier(ts);
}

void Garburator::postvisitTypeSpecifier(TypeSpecifier *ts) {
  if (stackClassStack.empty() || stackClassStack.back() != ts) return;
  stackClassStack.pop_back();
}

// returns true if rewritten
bool Garburator::rewriteTypeSpecifier(TypeSpecifier *ts, bool bracket) {
  if (isCOMPtr(ts)) {
    // isCOMPtr gurantees the following will work
    PQ_template *pqtemp = ts->asTS_name()->name->asPQ_template();
    remove_TemplateWrapper(pqtemp, bracket);
    return true;
  }
  return false;
}

void Garburator::insertInitializers(var_list::iterator begin, 
                                    var_list::iterator end,
                                    CPPSourceLoc const &csl,
                                    // use : instead of ,?
                                    bool firstInitializer,
                                    bool appendComma) 
  throw (x_match)
{
  // don't bother with an empty list
  if (begin == end) return;
  if (!csl.hasExactPosition()) {
    cerr << toString(csl.loc()) << ": Can't insert initializers." << endl;
    xmatch(csl.hasExactPosition());
  }
  std::stringstream ss;
  if (!appendComma) ss << (firstInitializer ? ": ": ", ");
  
  for (var_list::iterator it = begin; it != end; it++) {
    if (it != begin) ss << ", ";
    ss << (*it)->name << "(nsnull)";
  }
  if (appendComma) ss << ", ";
  patcher.insertBefore(csl, ss.str());
}

bool Garburator::visitFunction(Function *f) {
  if (f->nameAndParams->var->isImplicitMemberFunc()) return false;
  inFunction = true;
  try {
    var_list comPtrs;
    xmatch(!stackClassStack.empty() 
           && f->nameAndParams->var->type->asFunctionType()->isConstructor());
    for (ASTListIterNC<Member> i(SOME(stackClassStack.back())->members->list);
         ! i.isDone();
         i.adv()) {
      MR_decl *d = i.data()->ifMR_decl();
      if (!(d && isCOMPtr(d->d->spec))) continue;
  
      for (FakeList<Declarator>* decl = d->d->decllist;
       ! decl->isEmpty();
       decl = decl->butFirst()) {
        Variable *v = decl->first()->var;
        if (! v->type->isFunctionType() &&
	    ! v->type->isPtrOrRef() &&
            v->scope == f->nameAndParams->var->scope) {
          comPtrs.push_back(v);
        }
      }
    
    }
    var_list::iterator begin = comPtrs.begin();
    // remove members that already get initialized
    var_list::iterator it = begin;
    // don't init things that are already initialized
    // TODO: use this to figure out how to insert initializers
    // in correct order
    SourceLoc endloc = f->body->loc;
    for (FakeList<MemberInit>* i = f->inits;
         ! (i->isEmpty() || it == comPtrs.end());
         i = i->butFirst()) {
      MemberInit *mi = i->first();
      // my hack to figure out if initialzier is generated
      if (mi->endloc == SL_UNKNOWN) continue;
      endloc = mi->endloc;
      Variable *m = mi->member;

      if (m->type->isPtrOrRef() 
          || !isCOMPtr(m->type)) continue;

      do {
        if (m != *it) continue;
        insertInitializers(begin, it, mi->loc,
                           i == f->inits, true);
        // scroll past last element to be printed
        begin = it;
        begin++;
        cerr << toString(mi->name->loc) << ": " 
             << m->name << " is already initialized" 
             << endl;
        break;
      } while (++it != comPtrs.end());
    }
    UnboxedLoc uloc;
    // figure out where func ends..add stuff
    CPPSourceLoc csl = endloc;
    if (!csl.hasExactPosition()) {
      cerr << toString(csl.loc()) << ": Can't insert initializers." << endl;
    }
    xmatch(csl.hasExactPosition() && endloc != SL_UNKNOWN
           && begin != comPtrs.end());
    StringRef file = uloc.set(csl.loc());

    // if start points at begining of line, append stuff to previous line
    // SourceLoc is indexed from 1
    if (uloc.col == 1) --uloc.line;
    std::string str = patcher.getLine(uloc.line, file);
    if (uloc.col == 1) {
      uloc.col = str.size() + 1; 
    }
    for(; uloc.col - 1 && isspace(str[uloc.col - 2]);
        --uloc.col) {
    }
    csl.overrideLoc(uloc.toSourceLoc(file));
    insertInitializers(begin, comPtrs.end(), csl,
                       endloc == f->body->loc, false);
  } catch (x_match &) {
  }
  return true;
}

void Garburator::postvisitFunction(Function *f) {
  inFunction = false;
  localSet.clear();
}

Garburator::COMPTR_TYPE Garburator::isCOMPtr(TypeSpecifier *ts) {
  TS_name *tsname = ts->ifTS_name();
  if (!tsname) return COMPTR_TYPE_NONE;
  PQ_template *pqtemp = tsname->name->ifPQ_template();
  if (!pqtemp) return COMPTR_TYPE_NONE;

  if (pqtemp->name == str_already_AddRefed) return COMPTR_TYPE_ALREADY_ADDREFED;

  return (onStack() && pqtemp->name == str_nsCOMPtr) 
    ? COMPTR_TYPE_COMPTR 
    : COMPTR_TYPE_NONE;
}

Garburator::COMPTR_TYPE Garburator::isCOMPtr(Type *t) {
  t = skipPtrOrRef(t);
  if (!t->isCompoundType()) return COMPTR_TYPE_NONE;
  CompoundType *ct = t->asCompoundType();
  
  if (ct->name == str_nsCOMPtr) return COMPTR_TYPE_COMPTR;
  
  return ct->name == str_already_AddRefed ? COMPTR_TYPE_ALREADY_ADDREFED : COMPTR_TYPE_NONE;
}

bool Garburator::visitE_funCall(E_funCall *e) {
  if (!onStack()) return true;
  addCasts(e);
  addConversions(e);
  return true;
}

inline PQName *expr2name(Expression *eIn) {
  Expression *e = expr2obj(eIn);
  if (!e) return NULL;
  switch (e->kind()) {
  case Expression::E_VARIABLE:
    return e->asE_variable()->name;
  case Expression::E_FIELDACC:
    return e->asE_fieldAccC()->fieldName;
  default:
    return NULL;
  }
}

// Heap COMPtrs that are passed down to methods that used to take COMPTr<T>&
// and now take T*&, need to be wrapped in *getter_AddRefs()
void Garburator::addConversions(E_funCall *e) {
  Expression *eFunc = expr2obj(e->func);
  Variable *vFunc = expr2var(eFunc);
  PQName *name = expr2name(eFunc);
  if (!vFunc || vFunc->isImplicitMemberFunc()
      || vFunc->name == str_getter_AddRefs
      || vFunc->name == str_address_of
      || vFunc->name == str_swap
      || !name
      || !name->isPQ_name()) {
    return;
  }
  FunctionType *t = skipPtrOrRef(vFunc->type)->asFunctionType();

  SObjListIterNC<Variable> params(t->params);
  if (t->isMethod()) params.adv();

  for (FakeList<ArgExpression>* args = e->args;
       ! args->isEmpty() && !params.isDone();
       args = args->butFirst(), params.adv()) {
    Expression *argExpr = args->first()->expr;
    Type *paramType = params.data()->type;
    if (!paramType->isPtrOrRef()) continue;

    Variable *paramVar = params.data();
    Type *t = paramVar->type->getAtType();
    if (!t->isCVAtomicType()) continue;
    
    AtomicType *aArg = t->asCVAtomicType()->atomic;
    if (!aArg->isCompoundType()) continue;
    if (aArg->asCompoundType()->name != str_nsCOMPtr) continue;
    
    // this transform only applies to non-local vars
    if (isStackVar(expr2var(argExpr))) continue; 
    // usually calls to comptr& params are blacklisted
    if (!blacklister.allowedToRewrite(paramVar)) continue;

    CPPSourceLoc cslArg(argExpr->loc);
    CPPSourceLoc endcslArg(argExpr->endloc);
    if (!(cslArg.hasExactPosition() && endcslArg.hasExactPosition())) {
      cerr << toString(cslArg.loc()) << ": Can't insert getter_AddRefs"
           << endl;
      continue;
    }
    PairLoc pairArg(cslArg, endcslArg);
    std::string strObj;
    std::stringstream ss;
    // COMPtr passed as &COMPtr to comptr* params
    if (E_addrOf *aof = argExpr->ifE_addrOf()) {
      Expression *argExpr = aof->expr;
      Variable *argVar = expr2var(argExpr); 
      if (!argVar || isStackVar(argVar)) continue; 
      CPPSourceLoc cslObj(argExpr->loc);
      CPPSourceLoc endcslObj(argExpr->endloc);
      if (!(cslObj.hasExactPosition() && endcslObj.hasExactPosition())) {
        cerr << toString(cslArg.loc()) << ": Can't insert getter_AddRefs"
             << endl;
        continue; 
      }
      PairLoc pairLoc(cslObj, endcslObj);
      strObj = patcher.getRange(pairLoc);
    // COMPtr passed as itself to comptr& params
    } else if (paramType->isReferenceType()) {
      strObj = patcher.getRange(pairArg);
      ss << "*" ;
    } else {
      continue;
    }
    ss << str_getter_AddRefs << "(" << strObj << ")";
    patcher.printPatch(ss.str(), pairArg);    
  }
}

// Method params (nsISupports**|void**) can't called as nsIFoo*f;method(&f)
// without inserting a downcast.
void Garburator::addCasts(E_funCall *e) {
  Variable *vFunc = expr2var(e->func);
  // sometimes the expressions get too hairy, fix those by hand if needed
  if (!vFunc) {
    cerr << toString(e->func->loc) << ": " << "couldn't inspect fcall to insert downcasts" << endl;
    return;
  }
  FunctionType *t = skipPtrOrRef(vFunc->type)->asFunctionType();

  SObjListIter<Variable> params(t->params);
  if (t->isMethod()) params.adv();

  for (FakeList<ArgExpression>* args = e->args;
       ! args->isEmpty() && !params.isDone();
       args = args->butFirst(), params.adv()) {
    Expression *argExpr = args->first()->expr;

    Type *argType = skipPtrOrRef(argExpr->type);
    
    if (!argType->isCompoundType()) continue;

    CompoundType *ct = argType->asCompoundType();

    if (ct->name != str_nsGetterAddRefs) continue;

    Type *innerType = ct->templateInfo()->arguments.first()->getType();
    CompoundType *ctInner = innerType->asCompoundType();
    StringRef paramTypeName = ctInner->instName;

    Variable *argArgVar = NULL;
    // this rewrite only applies to local vars
    if (E_funCall *funcExpr = argExpr->ifE_funCall()) {
      Variable *argVar = expr2var(funcExpr->func);
      if (argVar->name != str_getter_AddRefs) continue;
      
      argArgVar = expr2var(funcExpr->args->first()->expr);
    } else if (E_constructor *ctorExpr = argExpr->ifE_constructor()) {
      argArgVar = expr2var(ctorExpr->args->first()->expr);
    }
    if (!argArgVar || !isStackVar(argArgVar)) continue;
    
    // if we got here, then we are dealing with getter_AddRefs()
    // so now check the function args for parameter type
    // to see if a cast is needed
    Type *paramType = params.data()->type;
    int indirectionCounter = 0;
    while(paramType->isPtrOrRef()) {
      if (paramType->isPointerType()) indirectionCounter++;
      paramType = paramType->getAtType();
    }
    if (paramType->isCVAtomicType()) {

      AtomicType *aArg = paramType->asCVAtomicType()->atomic;
      if (aArg->isCompoundType()) {
	CompoundType *ctArg = aArg->asCompoundType();
	
	if (paramTypeName == ctArg->instName) continue;
      }
    }
    std::stringstream ss;
    ss << "(" << paramType->toString();
    while (indirectionCounter--) ss << "*";
    ss << ")";

    CPPSourceLoc cslArg(argExpr->loc);
    if (!cslArg.hasExactPosition()) {
      cerr << toString(cslArg.loc()) << ": Can't insert cast: " << ss.str()
           << endl;
      continue;
    }

    patcher.insertBefore(cslArg, ss.str());
  }
  return;
}

void Garburator::rewrite_getter_AddRefs(E_funCall *e) {
  Variable *vFcall = expr2var(e->func);

  // only remove getter_AddRefs & address_of
  if (vFcall->name != str_getter_AddRefs 
      && vFcall->name != str_address_of) return;

  return rewrite_getter_AddRefs(e, e->args->first()->expr, 
				vFcall->name == str_getter_AddRefs);
}

// fcallCtor is the wrapper..ie getter_AddRefs
// arg is the contents ..ie foo in getter_AddRefs(foo)
// stackOnly is true if only stack vars are to be rewritten
void Garburator::rewrite_getter_AddRefs(Expression *fcallCtor, Expression *arg,
					bool stackOnly) {
  CPPSourceLoc outerS(fcallCtor->loc);
  CPPSourceLoc outerE(fcallCtor->endloc);

  // only rewrite stack variables
  Variable *v = expr2var(arg);

  // don't rewrite getter_AddRefs for heap vars
  bool isLocal = isStackVar(v);
  if (stackOnly && !isLocal) return;
  
  CPPSourceLoc innerS(arg->loc);
  CPPSourceLoc innerE(arg->endloc);
  PairLoc innerPair(innerS, innerE);
  PairLoc outerPair(outerS, outerE);
  if (!(innerPair.hasExactPosition() && outerPair.hasExactPosition())) {
    cerr << toString(outerS.loc()) << ": Can't rewrite wrapper function"
         << endl;
    return;
  }
  std::string strInner = patcher.getRange(innerPair);
  std::stringstream ss;
  // if doing address_of on a heap var..change it to getter_AddRefs
  if (!isLocal) {
    ss << str_getter_AddRefs << '(' << strInner << ')';
  } else {
    ss << '&' << strInner;
  }
  patcher.printPatch(ss.str(), outerPair);
  return;
}


// Pulls out obj out of obj->
static Expression* getReceiverExpr(Expression *e) {
  switch(e->kind()) {
  case Expression::E_DEREF:
    return getReceiverExpr(e->asE_deref()->ptr);
  default:
    return e;
  }
}

void Garburator::postvisitE_funCall(E_funCall *e) {
  static const StringRef str_get = globalStrTable("get");
  static const StringRef str_forget = globalStrTable("forget");
  if (!onStack()) return;

  Expression *func = e->func;
  Variable *v = expr2var(func);
  if (!v) return;

  E_fieldAcc *f = func->ifE_fieldAcc();
  
  // do nsComPtr::swap && nsCOMPtr::get swap
  if (f && (v->name == str_swap || v->name == str_get || v->name == str_forget)) {
    // don't rewrite this for heap objects
    COMPTR_TYPE comType = isCOMPtr(f->obj->type);
    if (!comType) return;
    Expression *obj = getReceiverExpr(f->obj);
    if (comType == COMPTR_TYPE_COMPTR) {
      Variable *v = expr2var(obj);
      if (!isStackVar(v)) return;
    }
    CPPSourceLoc csl(e->loc);
    CPPSourceLoc endcsl(e->endloc);
    PairLoc fcallLoc(csl, endcsl);
    
    CPPSourceLoc cslObj(obj->loc);
    CPPSourceLoc endcslObj(obj->endloc);
    PairLoc objLoc(cslObj, endcslObj);      
    if ( !(fcallLoc.hasExactPosition() && objLoc.hasExactPosition())) {
      cerr << toString(csl.loc()) << ": Can't rewrite ." << v->name << "()"
           << endl;
      return;
    }
    
    if (v->name == str_swap) {
      Expression *arg = e->args->first()->expr;
      
      CPPSourceLoc cslArg(arg->loc);
      CPPSourceLoc endcslArg(arg->endloc);
      PairLoc argLoc(cslArg, endcslArg);      
      if (! argLoc.hasExactPosition()) {
        cerr << toString(cslArg.loc()) << ": Can't rewrite ." << v->name << "()"
           << endl;
	return;
      }
      patcher.printPatch("swap(" + patcher.getRange(objLoc) 
			 + ", " + patcher.getRange(argLoc)
			 + ")", fcallLoc);
    } else if (v->name == str_get 
               || (v->name == str_forget && e->args->isEmpty())) {
      patcher.printPatch(patcher.getRange(objLoc), fcallLoc);
    } else if (v->name == str_forget) {
      Expression *arg = e->args->first()->expr;
      std::stringstream ss;
      if (arg->isE_addrOf()) {
        arg = arg->asE_addrOf()->expr;
      } else {
        ss << "*";
      }
      CPPSourceLoc cslArg(arg->loc);
      CPPSourceLoc endcslArg(arg->endloc);
      PairLoc argLoc(cslArg, endcslArg);      
      if (!argLoc.hasExactPosition()) {
        cerr << toString(cslArg.loc()) << ": Can't rewrite ." << v->name << "()"
           << endl;
	return;
      }
      // anything more complicated needs wrapping
      bool needsBrackets = !(arg->isE_variable() || arg->isE_fieldAcc());
      if (needsBrackets)
        ss << "(";
      ss << patcher.getRange(argLoc);
      if (needsBrackets)
        ss << ")";
      ss << " = " << patcher.getRange(objLoc);
      patcher.printPatch(ss.str(), fcallLoc);      
    }
  } else {
    rewrite_getter_AddRefs(e);
  }
}

bool Garburator::visitD_func(D_func *df) {
  for (FakeList<ASTTypeId>* params = df->params;
       ! params->isEmpty();
       params = params->butFirst()) {
    ASTTypeId *p = params->first();
    if (isCOMPtr(p->spec)
	&& !blacklister.allowedToRewrite(p->decl->var)) continue;
    p->traverse(this->loweredVisitor);
  }
  return false;
}

// Processes function declarations to avoid rewriting blacklisted params
bool Garburator::visitDeclarator(Declarator *d) {
  Variable *v = d->var;
  Type *t = v->type;
  if (d->decl->isD_func() && t->isFunctionType()
      && t->asFunctionType()->isConstructor()) {
    return visitD_func(d->decl->asD_func());
  }
  if (!onStack()) return true;

  if (!blacklister.allowedToRewrite(v)) return false;
    
  if (isCOMPtr(v->type)) localSet.insert(d->var);

  return true;
}

bool Garburator::visitE_constructor(E_constructor *c) {
  if (!onStack()) return true;
  // if this is an nsCOMPtr constructor, try rewrite the type as a cast
  return !rewriteTypeSpecifier(c->spec, true);
}

//the following 2 functions mark func declaration as onStack
bool Garburator::visitDeclaration(Declaration *d) {
  Variable *v = d->decllist ? d->decllist->first()->var : NULL;

  if (!v) return true;

  if (v->type->isFunctionType()) {
    inDecl = true; 
  } else if (onStack() && !blacklister.allowedToRewrite(v)) {
    return false;
  }
  return true;
}

void Garburator::postvisitDeclaration(Declaration *d) {
  Variable *v = d->decllist ? d->decllist->first()->var : NULL;
  if (v && v->type->isFunctionType())
    inDecl = false;
}

// process variable declarations in function bodies
// overrides visitor traversals to avoid rewriting too much through the visitor
// insert *before variable decls when multiple vars are declared
bool Garburator::visitS_decl(S_decl *s) {
  Declaration *d = s->decl;
  bool isComptr = isCOMPtr(d->spec);
  // visit stuff manually
  for (FakeList<Declarator>* vars = s->decl->decllist;
       ! vars->isEmpty();
       vars = vars->butFirst()) {
    Declarator *decl = vars->first();
    S_expr *sexpr = decl->ctorStatement ? decl->ctorStatement->ifS_expr() : NULL;
    E_constructor *ctor =   sexpr ? sexpr->expr->expr->ifE_constructor() : NULL;
    visitDeclarator(decl);
    if (isComptr && ctor && ctor->args->isEmpty())
      {
      // no initializer, have to rewrite stuff to nsnull
      IDeclarator *idecl = decl->decl;
      CPPSourceLoc csl = idecl->loc;
      if (csl.hasExactPosition()) {
        patcher.insertBefore(csl, " = nsnull", 
                             idecl->getDeclaratorId()->toString().length());
      } else {
        cerr << toString(csl.loc()) << ": Could not add '= nsnull'" << endl;
      }
    } else {
      decl->traverse(this->loweredVisitor);
    }
    if (!isComptr) continue;

    // rewrite the type decl for first var
    if (s->decl->decllist == vars) {
      if (!decl->var->type->isPtrOrRef() && visitDeclaration(d)) {
	rewriteTypeSpecifier(d->spec);
      }
    }
    // for adding * to pointers in a multi-var decl
    else {
      CPPSourceLoc csl(decl->decl->loc);
      if (csl.hasExactPosition()) {
        patcher.insertBefore(csl, "*");
      } else {
        cerr << toString(csl.loc()) 
             << ": Could not add * to turn declaration into pointer" << endl;
      }
    }
  }
  return false;
}

void Garburator::postvisitE_constructor(E_constructor *e) {
  TS_name *ts = e->spec->ifTS_name();
  if (!ts) return;
  PQ_template *pq = ts->name->ifPQ_template();
  if (!pq || pq->name != str_nsGetterAddRefs) return;
  
  //do the rest of the getter_AddRefs magic
  rewrite_getter_AddRefs(e, e->args->first()->expr, true);
}

bool Garburator::visitE_cast(E_cast *e) {
  e->expr->traverse(this->loweredVisitor);
  return false;
}

bool Garburator::visitE_keywordCast(E_keywordCast *e) {
  e->expr->traverse(this->loweredVisitor);
  return false;
}
