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