forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			436 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- TransAutoreleasePool.cpp - Transformations to ARC mode -----------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// rewriteAutoreleasePool:
 | 
						|
//
 | 
						|
// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
 | 
						|
//
 | 
						|
//  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 | 
						|
//  ...
 | 
						|
//  [pool release];
 | 
						|
// ---->
 | 
						|
//  @autorelease {
 | 
						|
//  ...
 | 
						|
//  }
 | 
						|
//
 | 
						|
// An NSAutoreleasePool will not be touched if:
 | 
						|
// - There is not a corresponding -release/-drain in the same scope
 | 
						|
// - Not all references of the NSAutoreleasePool variable can be removed
 | 
						|
// - There is a variable that is declared inside the intended @autorelease scope
 | 
						|
//   which is also used outside it.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "Transforms.h"
 | 
						|
#include "Internals.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/Basic/SourceManager.h"
 | 
						|
#include "clang/Sema/SemaDiagnostic.h"
 | 
						|
#include <map>
 | 
						|
 | 
						|
using namespace clang;
 | 
						|
using namespace arcmt;
 | 
						|
using namespace trans;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
 | 
						|
  Decl *Dcl;
 | 
						|
  SmallVectorImpl<ObjCMessageExpr *> &Releases;
 | 
						|
 | 
						|
public:
 | 
						|
  ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
 | 
						|
    : Dcl(D), Releases(releases) { }
 | 
						|
 | 
						|
  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
 | 
						|
    if (!E->isInstanceMessage())
 | 
						|
      return true;
 | 
						|
    if (E->getMethodFamily() != OMF_release)
 | 
						|
      return true;
 | 
						|
    Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
 | 
						|
    if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
 | 
						|
      if (DE->getDecl() == Dcl)
 | 
						|
        Releases.push_back(E);
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class AutoreleasePoolRewriter
 | 
						|
                         : public RecursiveASTVisitor<AutoreleasePoolRewriter> {
 | 
						|
public:
 | 
						|
  AutoreleasePoolRewriter(MigrationPass &pass)
 | 
						|
    : Body(nullptr), Pass(pass) {
 | 
						|
    PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
 | 
						|
    DrainSel = pass.Ctx.Selectors.getNullarySelector(
 | 
						|
                                                 &pass.Ctx.Idents.get("drain"));
 | 
						|
  }
 | 
						|
 | 
						|
  void transformBody(Stmt *body, Decl *ParentD) {
 | 
						|
    Body = body;
 | 
						|
    TraverseStmt(body);
 | 
						|
  }
 | 
						|
  
 | 
						|
  ~AutoreleasePoolRewriter() {
 | 
						|
    SmallVector<VarDecl *, 8> VarsToHandle;
 | 
						|
 | 
						|
    for (std::map<VarDecl *, PoolVarInfo>::iterator
 | 
						|
           I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
 | 
						|
      VarDecl *var = I->first;
 | 
						|
      PoolVarInfo &info = I->second;
 | 
						|
 | 
						|
      // Check that we can handle/rewrite all references of the pool.
 | 
						|
 | 
						|
      clearRefsIn(info.Dcl, info.Refs);
 | 
						|
      for (SmallVectorImpl<PoolScope>::iterator
 | 
						|
             scpI = info.Scopes.begin(),
 | 
						|
             scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
 | 
						|
        PoolScope &scope = *scpI;
 | 
						|
        clearRefsIn(*scope.Begin, info.Refs);
 | 
						|
        clearRefsIn(*scope.End, info.Refs);
 | 
						|
        clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
 | 
						|
      }
 | 
						|
 | 
						|
      // Even if one reference is not handled we will not do anything about that
 | 
						|
      // pool variable.
 | 
						|
      if (info.Refs.empty())
 | 
						|
        VarsToHandle.push_back(var);
 | 
						|
    }
 | 
						|
 | 
						|
    for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
 | 
						|
      PoolVarInfo &info = PoolVars[VarsToHandle[i]];
 | 
						|
 | 
						|
      Transaction Trans(Pass.TA);
 | 
						|
 | 
						|
      clearUnavailableDiags(info.Dcl);
 | 
						|
      Pass.TA.removeStmt(info.Dcl);
 | 
						|
 | 
						|
      // Add "@autoreleasepool { }"
 | 
						|
      for (SmallVectorImpl<PoolScope>::iterator
 | 
						|
             scpI = info.Scopes.begin(),
 | 
						|
             scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
 | 
						|
        PoolScope &scope = *scpI;
 | 
						|
        clearUnavailableDiags(*scope.Begin);
 | 
						|
        clearUnavailableDiags(*scope.End);
 | 
						|
        if (scope.IsFollowedBySimpleReturnStmt) {
 | 
						|
          // Include the return in the scope.
 | 
						|
          Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
 | 
						|
          Pass.TA.removeStmt(*scope.End);
 | 
						|
          Stmt::child_iterator retI = scope.End;
 | 
						|
          ++retI;
 | 
						|
          SourceLocation afterSemi = findLocationAfterSemi((*retI)->getLocEnd(),
 | 
						|
                                                           Pass.Ctx);
 | 
						|
          assert(afterSemi.isValid() &&
 | 
						|
                 "Didn't we check before setting IsFollowedBySimpleReturnStmt "
 | 
						|
                 "to true?");
 | 
						|
          Pass.TA.insertAfterToken(afterSemi, "\n}");
 | 
						|
          Pass.TA.increaseIndentation(
 | 
						|
                                SourceRange(scope.getIndentedRange().getBegin(),
 | 
						|
                                            (*retI)->getLocEnd()),
 | 
						|
                                      scope.CompoundParent->getLocStart());
 | 
						|
        } else {
 | 
						|
          Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
 | 
						|
          Pass.TA.replaceStmt(*scope.End, "}");
 | 
						|
          Pass.TA.increaseIndentation(scope.getIndentedRange(),
 | 
						|
                                      scope.CompoundParent->getLocStart());
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Remove rest of pool var references.
 | 
						|
      for (SmallVectorImpl<PoolScope>::iterator
 | 
						|
             scpI = info.Scopes.begin(),
 | 
						|
             scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
 | 
						|
        PoolScope &scope = *scpI;
 | 
						|
        for (SmallVectorImpl<ObjCMessageExpr *>::iterator
 | 
						|
               relI = scope.Releases.begin(),
 | 
						|
               relE = scope.Releases.end(); relI != relE; ++relI) {
 | 
						|
          clearUnavailableDiags(*relI);
 | 
						|
          Pass.TA.removeStmt(*relI);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool VisitCompoundStmt(CompoundStmt *S) {
 | 
						|
    SmallVector<PoolScope, 4> Scopes;
 | 
						|
 | 
						|
    for (Stmt::child_iterator
 | 
						|
           I = S->body_begin(), E = S->body_end(); I != E; ++I) {
 | 
						|
      Stmt *child = getEssential(*I);
 | 
						|
      if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
 | 
						|
        if (DclS->isSingleDecl()) {
 | 
						|
          if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
 | 
						|
            if (isNSAutoreleasePool(VD->getType())) {
 | 
						|
              PoolVarInfo &info = PoolVars[VD];
 | 
						|
              info.Dcl = DclS;
 | 
						|
              collectRefs(VD, S, info.Refs);
 | 
						|
              // Does this statement follow the pattern:  
 | 
						|
              // NSAutoreleasePool * pool = [NSAutoreleasePool  new];
 | 
						|
              if (isPoolCreation(VD->getInit())) {
 | 
						|
                Scopes.push_back(PoolScope());
 | 
						|
                Scopes.back().PoolVar = VD;
 | 
						|
                Scopes.back().CompoundParent = S;
 | 
						|
                Scopes.back().Begin = I;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
 | 
						|
        if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
 | 
						|
          if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
 | 
						|
            // Does this statement follow the pattern:  
 | 
						|
            // pool = [NSAutoreleasePool  new];
 | 
						|
            if (isNSAutoreleasePool(VD->getType()) &&
 | 
						|
                isPoolCreation(bop->getRHS())) {
 | 
						|
              Scopes.push_back(PoolScope());
 | 
						|
              Scopes.back().PoolVar = VD;
 | 
						|
              Scopes.back().CompoundParent = S;
 | 
						|
              Scopes.back().Begin = I;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (Scopes.empty())
 | 
						|
        continue;
 | 
						|
 | 
						|
      if (isPoolDrain(Scopes.back().PoolVar, child)) {
 | 
						|
        PoolScope &scope = Scopes.back();
 | 
						|
        scope.End = I;
 | 
						|
        handlePoolScope(scope, S);
 | 
						|
        Scopes.pop_back();
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
private:
 | 
						|
  void clearUnavailableDiags(Stmt *S) {
 | 
						|
    if (S)
 | 
						|
      Pass.TA.clearDiagnostic(diag::err_unavailable,
 | 
						|
                              diag::err_unavailable_message,
 | 
						|
                              S->getSourceRange());
 | 
						|
  }
 | 
						|
 | 
						|
  struct PoolScope {
 | 
						|
    VarDecl *PoolVar;
 | 
						|
    CompoundStmt *CompoundParent;
 | 
						|
    Stmt::child_iterator Begin;
 | 
						|
    Stmt::child_iterator End;
 | 
						|
    bool IsFollowedBySimpleReturnStmt;
 | 
						|
    SmallVector<ObjCMessageExpr *, 4> Releases;
 | 
						|
 | 
						|
    PoolScope() : PoolVar(nullptr), CompoundParent(nullptr), Begin(), End(),
 | 
						|
                  IsFollowedBySimpleReturnStmt(false) { }
 | 
						|
 | 
						|
    SourceRange getIndentedRange() const {
 | 
						|
      Stmt::child_iterator rangeS = Begin;
 | 
						|
      ++rangeS;
 | 
						|
      if (rangeS == End)
 | 
						|
        return SourceRange();
 | 
						|
      Stmt::child_iterator rangeE = Begin;
 | 
						|
      for (Stmt::child_iterator I = rangeS; I != End; ++I)
 | 
						|
        ++rangeE;
 | 
						|
      return SourceRange((*rangeS)->getLocStart(), (*rangeE)->getLocEnd());
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
 | 
						|
    ASTContext &Ctx;
 | 
						|
    SourceRange ScopeRange;
 | 
						|
    SourceLocation &referenceLoc, &declarationLoc;
 | 
						|
 | 
						|
  public:
 | 
						|
    NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
 | 
						|
                         SourceLocation &referenceLoc,
 | 
						|
                         SourceLocation &declarationLoc)
 | 
						|
      : Ctx(ctx), referenceLoc(referenceLoc),
 | 
						|
        declarationLoc(declarationLoc) {
 | 
						|
      ScopeRange = SourceRange((*scope.Begin)->getLocStart(),
 | 
						|
                               (*scope.End)->getLocStart());
 | 
						|
    }
 | 
						|
 | 
						|
    bool VisitDeclRefExpr(DeclRefExpr *E) {
 | 
						|
      return checkRef(E->getLocation(), E->getDecl()->getLocation());
 | 
						|
    }
 | 
						|
 | 
						|
    bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
 | 
						|
      return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
 | 
						|
    }
 | 
						|
 | 
						|
    bool VisitTagTypeLoc(TagTypeLoc TL) {
 | 
						|
      return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
 | 
						|
    }
 | 
						|
 | 
						|
  private:
 | 
						|
    bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
 | 
						|
      if (isInScope(declLoc)) {
 | 
						|
        referenceLoc = refLoc;
 | 
						|
        declarationLoc = declLoc;
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool isInScope(SourceLocation loc) {
 | 
						|
      if (loc.isInvalid())
 | 
						|
        return false;
 | 
						|
 | 
						|
      SourceManager &SM = Ctx.getSourceManager();
 | 
						|
      if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
 | 
						|
        return false;
 | 
						|
      return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
 | 
						|
    // Check that all names declared inside the scope are not used
 | 
						|
    // outside the scope.
 | 
						|
    {
 | 
						|
      bool nameUsedOutsideScope = false;
 | 
						|
      SourceLocation referenceLoc, declarationLoc;
 | 
						|
      Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
 | 
						|
      ++SI;
 | 
						|
      // Check if the autoreleasepool scope is followed by a simple return
 | 
						|
      // statement, in which case we will include the return in the scope.
 | 
						|
      if (SI != SE)
 | 
						|
        if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
 | 
						|
          if ((retS->getRetValue() == nullptr ||
 | 
						|
               isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
 | 
						|
              findLocationAfterSemi(retS->getLocEnd(), Pass.Ctx).isValid()) {
 | 
						|
            scope.IsFollowedBySimpleReturnStmt = true;
 | 
						|
            ++SI; // the return will be included in scope, don't check it.
 | 
						|
          }
 | 
						|
      
 | 
						|
      for (; SI != SE; ++SI) {
 | 
						|
        nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
 | 
						|
                                                     referenceLoc,
 | 
						|
                                              declarationLoc).TraverseStmt(*SI);
 | 
						|
        if (nameUsedOutsideScope)
 | 
						|
          break;
 | 
						|
      }
 | 
						|
 | 
						|
      // If not all references were cleared it means some variables/typenames/etc
 | 
						|
      // declared inside the pool scope are used outside of it.
 | 
						|
      // We won't try to rewrite the pool.
 | 
						|
      if (nameUsedOutsideScope) {
 | 
						|
        Pass.TA.reportError("a name is referenced outside the "
 | 
						|
            "NSAutoreleasePool scope that it was declared in", referenceLoc);
 | 
						|
        Pass.TA.reportNote("name declared here", declarationLoc);
 | 
						|
        Pass.TA.reportNote("intended @autoreleasepool scope begins here",
 | 
						|
                           (*scope.Begin)->getLocStart());
 | 
						|
        Pass.TA.reportNote("intended @autoreleasepool scope ends here",
 | 
						|
                           (*scope.End)->getLocStart());
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Collect all releases of the pool; they will be removed.
 | 
						|
    {
 | 
						|
      ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
 | 
						|
      Stmt::child_iterator I = scope.Begin;
 | 
						|
      ++I;
 | 
						|
      for (; I != scope.End; ++I)
 | 
						|
        releaseColl.TraverseStmt(*I);
 | 
						|
    }
 | 
						|
 | 
						|
    PoolVars[scope.PoolVar].Scopes.push_back(scope);
 | 
						|
  }
 | 
						|
 | 
						|
  bool isPoolCreation(Expr *E) {
 | 
						|
    if (!E) return false;
 | 
						|
    E = getEssential(E);
 | 
						|
    ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
 | 
						|
    if (!ME) return false;
 | 
						|
    if (ME->getMethodFamily() == OMF_new &&
 | 
						|
        ME->getReceiverKind() == ObjCMessageExpr::Class &&
 | 
						|
        isNSAutoreleasePool(ME->getReceiverInterface()))
 | 
						|
      return true;
 | 
						|
    if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
 | 
						|
        ME->getMethodFamily() == OMF_init) {
 | 
						|
      Expr *rec = getEssential(ME->getInstanceReceiver());
 | 
						|
      if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
 | 
						|
        if (recME->getMethodFamily() == OMF_alloc &&
 | 
						|
            recME->getReceiverKind() == ObjCMessageExpr::Class &&
 | 
						|
            isNSAutoreleasePool(recME->getReceiverInterface()))
 | 
						|
          return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
 | 
						|
    if (!S) return false;
 | 
						|
    S = getEssential(S);
 | 
						|
    ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
 | 
						|
    if (!ME) return false;
 | 
						|
    if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
 | 
						|
      Expr *rec = getEssential(ME->getInstanceReceiver());
 | 
						|
      if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
 | 
						|
        if (dref->getDecl() == poolVar)
 | 
						|
          return ME->getMethodFamily() == OMF_release ||
 | 
						|
                 ME->getSelector() == DrainSel;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
 | 
						|
    return IDecl && IDecl->getIdentifier() == PoolII;
 | 
						|
  }
 | 
						|
 | 
						|
  bool isNSAutoreleasePool(QualType Ty) {
 | 
						|
    QualType pointee = Ty->getPointeeType();
 | 
						|
    if (pointee.isNull())
 | 
						|
      return false;
 | 
						|
    if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
 | 
						|
      return isNSAutoreleasePool(interT->getDecl());
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  static Expr *getEssential(Expr *E) {
 | 
						|
    return cast<Expr>(getEssential((Stmt*)E));
 | 
						|
  }
 | 
						|
  static Stmt *getEssential(Stmt *S) {
 | 
						|
    if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S))
 | 
						|
      S = EWC->getSubExpr();
 | 
						|
    if (Expr *E = dyn_cast<Expr>(S))
 | 
						|
      S = E->IgnoreParenCasts();
 | 
						|
    return S;
 | 
						|
  }
 | 
						|
 | 
						|
  Stmt *Body;
 | 
						|
  MigrationPass &Pass;
 | 
						|
 | 
						|
  IdentifierInfo *PoolII;
 | 
						|
  Selector DrainSel;
 | 
						|
  
 | 
						|
  struct PoolVarInfo {
 | 
						|
    DeclStmt *Dcl;
 | 
						|
    ExprSet Refs;
 | 
						|
    SmallVector<PoolScope, 2> Scopes;
 | 
						|
 | 
						|
    PoolVarInfo() : Dcl(nullptr) { }
 | 
						|
  };
 | 
						|
 | 
						|
  std::map<VarDecl *, PoolVarInfo> PoolVars;
 | 
						|
};
 | 
						|
 | 
						|
} // anonymous namespace
 | 
						|
 | 
						|
void trans::rewriteAutoreleasePool(MigrationPass &pass) {
 | 
						|
  BodyTransform<AutoreleasePoolRewriter> trans(pass);
 | 
						|
  trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
 | 
						|
}
 |