1 //===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // removeRetainReleaseDealloc: 11 // 12 // Removes retain/release/autorelease/dealloc messages. 13 // 14 // return [[foo retain] autorelease]; 15 // ----> 16 // return foo; 17 // 18 //===----------------------------------------------------------------------===// 19 20 #include "Transforms.h" 21 #include "Internals.h" 22 #include "clang/Sema/SemaDiagnostic.h" 23 #include "clang/AST/ParentMap.h" 24 #include "clang/Lex/Lexer.h" 25 #include "clang/Basic/SourceManager.h" 26 27 using namespace clang; 28 using namespace arcmt; 29 using namespace trans; 30 31 namespace { 32 33 class RetainReleaseDeallocRemover : 34 public RecursiveASTVisitor<RetainReleaseDeallocRemover> { 35 Stmt *Body; 36 MigrationPass &Pass; 37 38 ExprSet Removables; 39 OwningPtr<ParentMap> StmtMap; 40 41 Selector DelegateSel, FinalizeSel; 42 43 public: 44 RetainReleaseDeallocRemover(MigrationPass &pass) 45 : Body(0), Pass(pass) { 46 DelegateSel = 47 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); 48 FinalizeSel = 49 Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); 50 } 51 52 void transformBody(Stmt *body) { 53 Body = body; 54 collectRemovables(body, Removables); 55 StmtMap.reset(new ParentMap(body)); 56 TraverseStmt(body); 57 } 58 59 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 60 switch (E->getMethodFamily()) { 61 default: 62 if (E->isInstanceMessage() && E->getSelector() == FinalizeSel) 63 break; 64 return true; 65 case OMF_autorelease: 66 if (isRemovable(E)) { 67 if (!isCommonUnusedAutorelease(E)) { 68 // An unused autorelease is badness. If we remove it the receiver 69 // will likely die immediately while previously it was kept alive 70 // by the autorelease pool. This is bad practice in general, leave it 71 // and emit an error to force the user to restructure his code. 72 Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " 73 "message; its receiver may be destroyed immediately", 74 E->getLocStart(), E->getSourceRange()); 75 return true; 76 } 77 } 78 // Pass through. 79 case OMF_retain: 80 case OMF_release: 81 if (E->getReceiverKind() == ObjCMessageExpr::Instance) 82 if (Expr *rec = E->getInstanceReceiver()) { 83 rec = rec->IgnoreParenImpCasts(); 84 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && 85 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 86 std::string err = "it is not safe to remove '"; 87 err += E->getSelector().getAsString() + "' message on " 88 "an __unsafe_unretained type"; 89 Pass.TA.reportError(err, rec->getLocStart()); 90 return true; 91 } 92 93 if (isGlobalVar(rec) && 94 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 95 std::string err = "it is not safe to remove '"; 96 err += E->getSelector().getAsString() + "' message on " 97 "a global variable"; 98 Pass.TA.reportError(err, rec->getLocStart()); 99 return true; 100 } 101 102 if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { 103 Pass.TA.reportError("it is not safe to remove 'retain' " 104 "message on the result of a 'delegate' message; " 105 "the object that was passed to 'setDelegate:' may not be " 106 "properly retained", rec->getLocStart()); 107 return true; 108 } 109 } 110 case OMF_dealloc: 111 break; 112 } 113 114 switch (E->getReceiverKind()) { 115 default: 116 return true; 117 case ObjCMessageExpr::SuperInstance: { 118 Transaction Trans(Pass.TA); 119 clearDiagnostics(E->getSuperLoc()); 120 if (tryRemoving(E)) 121 return true; 122 Pass.TA.replace(E->getSourceRange(), "self"); 123 return true; 124 } 125 case ObjCMessageExpr::Instance: 126 break; 127 } 128 129 Expr *rec = E->getInstanceReceiver(); 130 if (!rec) return true; 131 132 Transaction Trans(Pass.TA); 133 clearDiagnostics(rec->getExprLoc()); 134 135 ObjCMessageExpr *Msg = E; 136 Expr *RecContainer = Msg; 137 SourceRange RecRange = rec->getSourceRange(); 138 checkForGCDOrXPC(Msg, RecContainer, rec, RecRange); 139 140 if (Msg->getMethodFamily() == OMF_release && 141 isRemovable(RecContainer) && isInAtFinally(RecContainer)) { 142 // Change the -release to "receiver = nil" in a finally to avoid a leak 143 // when an exception is thrown. 144 Pass.TA.replace(RecContainer->getSourceRange(), RecRange); 145 std::string str = " = "; 146 str += getNilString(Pass.Ctx); 147 Pass.TA.insertAfterToken(RecRange.getEnd(), str); 148 return true; 149 } 150 151 if (!hasSideEffects(rec, Pass.Ctx)) { 152 if (tryRemoving(RecContainer)) 153 return true; 154 } 155 Pass.TA.replace(RecContainer->getSourceRange(), RecRange); 156 157 return true; 158 } 159 160 private: 161 /// \brief Checks for idioms where an unused -autorelease is common. 162 /// 163 /// Currently only returns true for this idiom which is common in property 164 /// setters: 165 /// 166 /// [backingValue autorelease]; 167 /// backingValue = [newValue retain]; // in general a +1 assign 168 /// 169 bool isCommonUnusedAutorelease(ObjCMessageExpr *E) { 170 Expr *Rec = E->getInstanceReceiver(); 171 if (!Rec) 172 return false; 173 174 Decl *RefD = getReferencedDecl(Rec); 175 if (!RefD) 176 return false; 177 178 Stmt *OuterS = E, *InnerS; 179 do { 180 InnerS = OuterS; 181 OuterS = StmtMap->getParent(InnerS); 182 } 183 while (OuterS && (isa<ParenExpr>(OuterS) || 184 isa<CastExpr>(OuterS) || 185 isa<ExprWithCleanups>(OuterS))); 186 187 if (!OuterS) 188 return false; 189 190 // Find next statement after the -autorelease. 191 192 Stmt::child_iterator currChildS = OuterS->child_begin(); 193 Stmt::child_iterator childE = OuterS->child_end(); 194 for (; currChildS != childE; ++currChildS) { 195 if (*currChildS == InnerS) 196 break; 197 } 198 if (currChildS == childE) 199 return false; 200 ++currChildS; 201 if (currChildS == childE) 202 return false; 203 204 Stmt *nextStmt = *currChildS; 205 if (!nextStmt) 206 return false; 207 nextStmt = nextStmt->IgnoreImplicit(); 208 209 // Check for "RefD = [+1 retained object];". 210 211 if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) { 212 if (RefD != getReferencedDecl(Bop->getLHS())) 213 return false; 214 if (isPlusOneAssign(Bop)) 215 return true; 216 } 217 return false; 218 } 219 220 Decl *getReferencedDecl(Expr *E) { 221 if (!E) 222 return 0; 223 224 E = E->IgnoreParenCasts(); 225 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) 226 return DRE->getDecl(); 227 if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) 228 return ME->getMemberDecl(); 229 if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E)) 230 return IRE->getDecl(); 231 232 return 0; 233 } 234 235 /// \brief Check if the retain/release is due to a GCD/XPC macro that are 236 /// defined as: 237 /// 238 /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; }) 239 /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; }) 240 /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; }) 241 /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; }) 242 /// 243 /// and return the top container which is the StmtExpr and the macro argument 244 /// expression. 245 void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer, 246 Expr *&Rec, SourceRange &RecRange) { 247 SourceLocation Loc = Msg->getExprLoc(); 248 if (!Loc.isMacroID()) 249 return; 250 SourceManager &SM = Pass.Ctx.getSourceManager(); 251 StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, 252 Pass.Ctx.getLangOpts()); 253 bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) 254 .Case("dispatch_retain", true) 255 .Case("dispatch_release", true) 256 .Case("xpc_retain", true) 257 .Case("xpc_release", true) 258 .Default(false); 259 if (!isGCDOrXPC) 260 return; 261 262 StmtExpr *StmtE = 0; 263 Stmt *S = Msg; 264 while (S) { 265 if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) { 266 StmtE = SE; 267 break; 268 } 269 S = StmtMap->getParent(S); 270 } 271 272 if (!StmtE) 273 return; 274 275 Stmt::child_range StmtExprChild = StmtE->children(); 276 if (!StmtExprChild) 277 return; 278 CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild); 279 if (!CompS) 280 return; 281 282 Stmt::child_range CompStmtChild = CompS->children(); 283 if (!CompStmtChild) 284 return; 285 DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild); 286 if (!DeclS) 287 return; 288 if (!DeclS->isSingleDecl()) 289 return; 290 VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()); 291 if (!VD) 292 return; 293 Expr *Init = VD->getInit(); 294 if (!Init) 295 return; 296 297 RecContainer = StmtE; 298 Rec = Init->IgnoreParenImpCasts(); 299 if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec)) 300 Rec = EWC->getSubExpr()->IgnoreParenImpCasts(); 301 RecRange = Rec->getSourceRange(); 302 if (SM.isMacroArgExpansion(RecRange.getBegin())) 303 RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin())); 304 if (SM.isMacroArgExpansion(RecRange.getEnd())) 305 RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd())); 306 } 307 308 void clearDiagnostics(SourceLocation loc) const { 309 Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, 310 diag::err_unavailable, 311 diag::err_unavailable_message, 312 loc); 313 } 314 315 bool isDelegateMessage(Expr *E) const { 316 if (!E) return false; 317 318 E = E->IgnoreParenCasts(); 319 320 // Also look through property-getter sugar. 321 if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) 322 E = pseudoOp->getResultExpr()->IgnoreImplicit(); 323 324 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) 325 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); 326 327 return false; 328 } 329 330 bool isInAtFinally(Expr *E) const { 331 assert(E); 332 Stmt *S = E; 333 while (S) { 334 if (isa<ObjCAtFinallyStmt>(S)) 335 return true; 336 S = StmtMap->getParent(S); 337 } 338 339 return false; 340 } 341 342 bool isRemovable(Expr *E) const { 343 return Removables.count(E); 344 } 345 346 bool tryRemoving(Expr *E) const { 347 if (isRemovable(E)) { 348 Pass.TA.removeStmt(E); 349 return true; 350 } 351 352 Stmt *parent = StmtMap->getParent(E); 353 354 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) 355 return tryRemoving(castE); 356 357 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) 358 return tryRemoving(parenE); 359 360 if (BinaryOperator * 361 bopE = dyn_cast_or_null<BinaryOperator>(parent)) { 362 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && 363 isRemovable(bopE)) { 364 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); 365 return true; 366 } 367 } 368 369 return false; 370 } 371 372 }; 373 374 } // anonymous namespace 375 376 void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { 377 BodyTransform<RetainReleaseDeallocRemover> trans(pass); 378 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 379 } 380