clang API Documentation

TransAutoreleasePool.cpp
Go to the documentation of this file.
00001 //===--- TransAutoreleasePool.cpp - Tranformations to ARC mode ------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // rewriteAutoreleasePool:
00011 //
00012 // Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
00013 //
00014 //  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00015 //  ...
00016 //  [pool release];
00017 // ---->
00018 //  @autorelease {
00019 //  ...
00020 //  }
00021 //
00022 // An NSAutoreleasePool will not be touched if:
00023 // - There is not a corresponding -release/-drain in the same scope
00024 // - Not all references of the NSAutoreleasePool variable can be removed
00025 // - There is a variable that is declared inside the intended @autorelease scope
00026 //   which is also used outside it.
00027 //
00028 //===----------------------------------------------------------------------===//
00029 
00030 #include "Transforms.h"
00031 #include "Internals.h"
00032 #include "clang/Sema/SemaDiagnostic.h"
00033 #include "clang/Basic/SourceManager.h"
00034 #include <map>
00035 
00036 using namespace clang;
00037 using namespace arcmt;
00038 using namespace trans;
00039 
00040 namespace {
00041 
00042 class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
00043   Decl *Dcl;
00044   SmallVectorImpl<ObjCMessageExpr *> &Releases;
00045 
00046 public:
00047   ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
00048     : Dcl(D), Releases(releases) { }
00049 
00050   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
00051     if (!E->isInstanceMessage())
00052       return true;
00053     if (E->getMethodFamily() != OMF_release)
00054       return true;
00055     Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
00056     if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
00057       if (DE->getDecl() == Dcl)
00058         Releases.push_back(E);
00059     }
00060     return true;
00061   }
00062 };
00063 
00064 }
00065 
00066 namespace {
00067 
00068 class AutoreleasePoolRewriter
00069                          : public RecursiveASTVisitor<AutoreleasePoolRewriter> {
00070 public:
00071   AutoreleasePoolRewriter(MigrationPass &pass)
00072     : Body(0), Pass(pass) {
00073     PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
00074     DrainSel = pass.Ctx.Selectors.getNullarySelector(
00075                                                  &pass.Ctx.Idents.get("drain"));
00076   }
00077 
00078   void transformBody(Stmt *body) {
00079     Body = body;
00080     TraverseStmt(body);
00081   }
00082   
00083   ~AutoreleasePoolRewriter() {
00084     SmallVector<VarDecl *, 8> VarsToHandle;
00085 
00086     for (std::map<VarDecl *, PoolVarInfo>::iterator
00087            I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
00088       VarDecl *var = I->first;
00089       PoolVarInfo &info = I->second;
00090 
00091       // Check that we can handle/rewrite all references of the pool.
00092 
00093       clearRefsIn(info.Dcl, info.Refs);
00094       for (SmallVectorImpl<PoolScope>::iterator
00095              scpI = info.Scopes.begin(),
00096              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
00097         PoolScope &scope = *scpI;
00098         clearRefsIn(*scope.Begin, info.Refs);
00099         clearRefsIn(*scope.End, info.Refs);
00100         clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
00101       }
00102 
00103       // Even if one reference is not handled we will not do anything about that
00104       // pool variable.
00105       if (info.Refs.empty())
00106         VarsToHandle.push_back(var);
00107     }
00108 
00109     for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
00110       PoolVarInfo &info = PoolVars[VarsToHandle[i]];
00111 
00112       Transaction Trans(Pass.TA);
00113 
00114       clearUnavailableDiags(info.Dcl);
00115       Pass.TA.removeStmt(info.Dcl);
00116 
00117       // Add "@autoreleasepool { }"
00118       for (SmallVectorImpl<PoolScope>::iterator
00119              scpI = info.Scopes.begin(),
00120              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
00121         PoolScope &scope = *scpI;
00122         clearUnavailableDiags(*scope.Begin);
00123         clearUnavailableDiags(*scope.End);
00124         if (scope.IsFollowedBySimpleReturnStmt) {
00125           // Include the return in the scope.
00126           Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
00127           Pass.TA.removeStmt(*scope.End);
00128           Stmt::child_iterator retI = scope.End;
00129           ++retI;
00130           SourceLocation afterSemi = findLocationAfterSemi((*retI)->getLocEnd(),
00131                                                            Pass.Ctx);
00132           assert(afterSemi.isValid() &&
00133                  "Didn't we check before setting IsFollowedBySimpleReturnStmt "
00134                  "to true?");
00135           Pass.TA.insertAfterToken(afterSemi, "\n}");
00136           Pass.TA.increaseIndentation(
00137                                 SourceRange(scope.getIndentedRange().getBegin(),
00138                                             (*retI)->getLocEnd()),
00139                                       scope.CompoundParent->getLocStart());
00140         } else {
00141           Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
00142           Pass.TA.replaceStmt(*scope.End, "}");
00143           Pass.TA.increaseIndentation(scope.getIndentedRange(),
00144                                       scope.CompoundParent->getLocStart());
00145         }
00146       }
00147 
00148       // Remove rest of pool var references.
00149       for (SmallVectorImpl<PoolScope>::iterator
00150              scpI = info.Scopes.begin(),
00151              scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
00152         PoolScope &scope = *scpI;
00153         for (SmallVectorImpl<ObjCMessageExpr *>::iterator
00154                relI = scope.Releases.begin(),
00155                relE = scope.Releases.end(); relI != relE; ++relI) {
00156           clearUnavailableDiags(*relI);
00157           Pass.TA.removeStmt(*relI);
00158         }
00159       }
00160     }
00161   }
00162 
00163   bool VisitCompoundStmt(CompoundStmt *S) {
00164     SmallVector<PoolScope, 4> Scopes;
00165 
00166     for (Stmt::child_iterator
00167            I = S->body_begin(), E = S->body_end(); I != E; ++I) {
00168       Stmt *child = getEssential(*I);
00169       if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
00170         if (DclS->isSingleDecl()) {
00171           if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
00172             if (isNSAutoreleasePool(VD->getType())) {
00173               PoolVarInfo &info = PoolVars[VD];
00174               info.Dcl = DclS;
00175               collectRefs(VD, S, info.Refs);
00176               // Does this statement follow the pattern:  
00177               // NSAutoreleasePool * pool = [NSAutoreleasePool  new];
00178               if (isPoolCreation(VD->getInit())) {
00179                 Scopes.push_back(PoolScope());
00180                 Scopes.back().PoolVar = VD;
00181                 Scopes.back().CompoundParent = S;
00182                 Scopes.back().Begin = I;
00183               }
00184             }
00185           }
00186         }
00187       } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
00188         if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
00189           if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
00190             // Does this statement follow the pattern:  
00191             // pool = [NSAutoreleasePool  new];
00192             if (isNSAutoreleasePool(VD->getType()) &&
00193                 isPoolCreation(bop->getRHS())) {
00194               Scopes.push_back(PoolScope());
00195               Scopes.back().PoolVar = VD;
00196               Scopes.back().CompoundParent = S;
00197               Scopes.back().Begin = I;
00198             }
00199           }
00200         }
00201       }
00202 
00203       if (Scopes.empty())
00204         continue;
00205 
00206       if (isPoolDrain(Scopes.back().PoolVar, child)) {
00207         PoolScope &scope = Scopes.back();
00208         scope.End = I;
00209         handlePoolScope(scope, S);
00210         Scopes.pop_back();
00211       }
00212     }
00213     return true;
00214   }
00215 
00216 private:
00217   void clearUnavailableDiags(Stmt *S) {
00218     if (S)
00219       Pass.TA.clearDiagnostic(diag::err_unavailable,
00220                               diag::err_unavailable_message,
00221                               S->getSourceRange());
00222   }
00223 
00224   struct PoolScope {
00225     VarDecl *PoolVar;
00226     CompoundStmt *CompoundParent;
00227     Stmt::child_iterator Begin;
00228     Stmt::child_iterator End;
00229     bool IsFollowedBySimpleReturnStmt;
00230     SmallVector<ObjCMessageExpr *, 4> Releases;
00231 
00232     PoolScope() : PoolVar(0), CompoundParent(0), Begin(), End(),
00233                   IsFollowedBySimpleReturnStmt(false) { }
00234 
00235     SourceRange getIndentedRange() const {
00236       Stmt::child_iterator rangeS = Begin;
00237       ++rangeS;
00238       if (rangeS == End)
00239         return SourceRange();
00240       Stmt::child_iterator rangeE = Begin;
00241       for (Stmt::child_iterator I = rangeS; I != End; ++I)
00242         ++rangeE;
00243       return SourceRange((*rangeS)->getLocStart(), (*rangeE)->getLocEnd());
00244     }
00245   };
00246 
00247   class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
00248     ASTContext &Ctx;
00249     SourceRange ScopeRange;
00250     SourceLocation &referenceLoc, &declarationLoc;
00251 
00252   public:
00253     NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
00254                          SourceLocation &referenceLoc,
00255                          SourceLocation &declarationLoc)
00256       : Ctx(ctx), referenceLoc(referenceLoc),
00257         declarationLoc(declarationLoc) {
00258       ScopeRange = SourceRange((*scope.Begin)->getLocStart(),
00259                                (*scope.End)->getLocStart());
00260     }
00261 
00262     bool VisitDeclRefExpr(DeclRefExpr *E) {
00263       return checkRef(E->getLocation(), E->getDecl()->getLocation());
00264     }
00265 
00266     bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
00267       return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
00268     }
00269 
00270     bool VisitTagTypeLoc(TagTypeLoc TL) {
00271       return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
00272     }
00273 
00274   private:
00275     bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
00276       if (isInScope(declLoc)) {
00277         referenceLoc = refLoc;
00278         declarationLoc = declLoc;
00279         return false;
00280       }
00281       return true;
00282     }
00283 
00284     bool isInScope(SourceLocation loc) {
00285       if (loc.isInvalid())
00286         return false;
00287 
00288       SourceManager &SM = Ctx.getSourceManager();
00289       if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
00290         return false;
00291       return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
00292     }
00293   };
00294 
00295   void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
00296     // Check that all names declared inside the scope are not used
00297     // outside the scope.
00298     {
00299       bool nameUsedOutsideScope = false;
00300       SourceLocation referenceLoc, declarationLoc;
00301       Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
00302       ++SI;
00303       // Check if the autoreleasepool scope is followed by a simple return
00304       // statement, in which case we will include the return in the scope.
00305       if (SI != SE)
00306         if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
00307           if ((retS->getRetValue() == 0 ||
00308                isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
00309               findLocationAfterSemi(retS->getLocEnd(), Pass.Ctx).isValid()) {
00310             scope.IsFollowedBySimpleReturnStmt = true;
00311             ++SI; // the return will be included in scope, don't check it.
00312           }
00313       
00314       for (; SI != SE; ++SI) {
00315         nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
00316                                                      referenceLoc,
00317                                               declarationLoc).TraverseStmt(*SI);
00318         if (nameUsedOutsideScope)
00319           break;
00320       }
00321 
00322       // If not all references were cleared it means some variables/typenames/etc
00323       // declared inside the pool scope are used outside of it.
00324       // We won't try to rewrite the pool.
00325       if (nameUsedOutsideScope) {
00326         Pass.TA.reportError("a name is referenced outside the "
00327             "NSAutoreleasePool scope that it was declared in", referenceLoc);
00328         Pass.TA.reportNote("name declared here", declarationLoc);
00329         Pass.TA.reportNote("intended @autoreleasepool scope begins here",
00330                            (*scope.Begin)->getLocStart());
00331         Pass.TA.reportNote("intended @autoreleasepool scope ends here",
00332                            (*scope.End)->getLocStart());
00333         return;
00334       }
00335     }
00336 
00337     // Collect all releases of the pool; they will be removed.
00338     {
00339       ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
00340       Stmt::child_iterator I = scope.Begin;
00341       ++I;
00342       for (; I != scope.End; ++I)
00343         releaseColl.TraverseStmt(*I);
00344     }
00345 
00346     PoolVars[scope.PoolVar].Scopes.push_back(scope);
00347   }
00348 
00349   bool isPoolCreation(Expr *E) {
00350     if (!E) return false;
00351     E = getEssential(E);
00352     ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
00353     if (!ME) return false;
00354     if (ME->getMethodFamily() == OMF_new &&
00355         ME->getReceiverKind() == ObjCMessageExpr::Class &&
00356         isNSAutoreleasePool(ME->getReceiverInterface()))
00357       return true;
00358     if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
00359         ME->getMethodFamily() == OMF_init) {
00360       Expr *rec = getEssential(ME->getInstanceReceiver());
00361       if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
00362         if (recME->getMethodFamily() == OMF_alloc &&
00363             recME->getReceiverKind() == ObjCMessageExpr::Class &&
00364             isNSAutoreleasePool(recME->getReceiverInterface()))
00365           return true;
00366       }
00367     }
00368 
00369     return false;
00370   }
00371 
00372   bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
00373     if (!S) return false;
00374     S = getEssential(S);
00375     ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
00376     if (!ME) return false;
00377     if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
00378       Expr *rec = getEssential(ME->getInstanceReceiver());
00379       if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
00380         if (dref->getDecl() == poolVar)
00381           return ME->getMethodFamily() == OMF_release ||
00382                  ME->getSelector() == DrainSel;
00383     }
00384 
00385     return false;
00386   }
00387 
00388   bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
00389     return IDecl && IDecl->getIdentifier() == PoolII;
00390   }
00391 
00392   bool isNSAutoreleasePool(QualType Ty) {
00393     QualType pointee = Ty->getPointeeType();
00394     if (pointee.isNull())
00395       return false;
00396     if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
00397       return isNSAutoreleasePool(interT->getDecl());
00398     return false;
00399   }
00400 
00401   static Expr *getEssential(Expr *E) {
00402     return cast<Expr>(getEssential((Stmt*)E));
00403   }
00404   static Stmt *getEssential(Stmt *S) {
00405     if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S))
00406       S = EWC->getSubExpr();
00407     if (Expr *E = dyn_cast<Expr>(S))
00408       S = E->IgnoreParenCasts();
00409     return S;
00410   }
00411 
00412   Stmt *Body;
00413   MigrationPass &Pass;
00414 
00415   IdentifierInfo *PoolII;
00416   Selector DrainSel;
00417   
00418   struct PoolVarInfo {
00419     DeclStmt *Dcl;
00420     ExprSet Refs;
00421     SmallVector<PoolScope, 2> Scopes;
00422 
00423     PoolVarInfo() : Dcl(0) { }
00424   };
00425 
00426   std::map<VarDecl *, PoolVarInfo> PoolVars;
00427 };
00428 
00429 } // anonymous namespace
00430 
00431 void trans::rewriteAutoreleasePool(MigrationPass &pass) {
00432   BodyTransform<AutoreleasePoolRewriter> trans(pass);
00433   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
00434 }