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 27using namespace clang; 28using namespace arcmt; 29using namespace trans; 30 31namespace { 32 33class 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 43public: 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 // An unused autorelease is badness. If we remove it the receiver 68 // will likely die immediately while previously it was kept alive 69 // by the autorelease pool. This is bad practice in general, leave it 70 // and emit an error to force the user to restructure his code. 71 Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " 72 "message; its receiver may be destroyed immediately", 73 E->getLocStart(), E->getSourceRange()); 74 return true; 75 } 76 // Pass through. 77 case OMF_retain: 78 case OMF_release: 79 if (E->getReceiverKind() == ObjCMessageExpr::Instance) 80 if (Expr *rec = E->getInstanceReceiver()) { 81 rec = rec->IgnoreParenImpCasts(); 82 if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && 83 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 84 std::string err = "it is not safe to remove '"; 85 err += E->getSelector().getAsString() + "' message on " 86 "an __unsafe_unretained type"; 87 Pass.TA.reportError(err, rec->getLocStart()); 88 return true; 89 } 90 91 if (isGlobalVar(rec) && 92 (E->getMethodFamily() != OMF_retain || isRemovable(E))) { 93 std::string err = "it is not safe to remove '"; 94 err += E->getSelector().getAsString() + "' message on " 95 "a global variable"; 96 Pass.TA.reportError(err, rec->getLocStart()); 97 return true; 98 } 99 100 if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { 101 Pass.TA.reportError("it is not safe to remove 'retain' " 102 "message on the result of a 'delegate' message; " 103 "the object that was passed to 'setDelegate:' may not be " 104 "properly retained", rec->getLocStart()); 105 return true; 106 } 107 } 108 case OMF_dealloc: 109 break; 110 } 111 112 switch (E->getReceiverKind()) { 113 default: 114 return true; 115 case ObjCMessageExpr::SuperInstance: { 116 Transaction Trans(Pass.TA); 117 clearDiagnostics(E->getSuperLoc()); 118 if (tryRemoving(E)) 119 return true; 120 Pass.TA.replace(E->getSourceRange(), "self"); 121 return true; 122 } 123 case ObjCMessageExpr::Instance: 124 break; 125 } 126 127 Expr *rec = E->getInstanceReceiver(); 128 if (!rec) return true; 129 130 Transaction Trans(Pass.TA); 131 clearDiagnostics(rec->getExprLoc()); 132 133 ObjCMessageExpr *Msg = E; 134 Expr *RecContainer = Msg; 135 SourceRange RecRange = rec->getSourceRange(); 136 checkForGCDOrXPC(Msg, RecContainer, rec, RecRange); 137 138 if (Msg->getMethodFamily() == OMF_release && 139 isRemovable(RecContainer) && isInAtFinally(RecContainer)) { 140 // Change the -release to "receiver = nil" in a finally to avoid a leak 141 // when an exception is thrown. 142 Pass.TA.replace(RecContainer->getSourceRange(), RecRange); 143 std::string str = " = "; 144 str += getNilString(Pass.Ctx); 145 Pass.TA.insertAfterToken(RecRange.getEnd(), str); 146 return true; 147 } 148 149 if (!hasSideEffects(rec, Pass.Ctx)) { 150 if (tryRemoving(RecContainer)) 151 return true; 152 } 153 Pass.TA.replace(RecContainer->getSourceRange(), RecRange); 154 155 return true; 156 } 157 158private: 159 /// \brief Check if the retain/release is due to a GCD/XPC macro that are 160 /// defined as: 161 /// 162 /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; }) 163 /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; }) 164 /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; }) 165 /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; }) 166 /// 167 /// and return the top container which is the StmtExpr and the macro argument 168 /// expression. 169 void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer, 170 Expr *&Rec, SourceRange &RecRange) { 171 SourceLocation Loc = Msg->getExprLoc(); 172 if (!Loc.isMacroID()) 173 return; 174 SourceManager &SM = Pass.Ctx.getSourceManager(); 175 StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, 176 Pass.Ctx.getLangOpts()); 177 bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) 178 .Case("dispatch_retain", true) 179 .Case("dispatch_release", true) 180 .Case("xpc_retain", true) 181 .Case("xpc_release", true) 182 .Default(false); 183 if (!isGCDOrXPC) 184 return; 185 186 StmtExpr *StmtE = 0; 187 Stmt *S = Msg; 188 while (S) { 189 if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) { 190 StmtE = SE; 191 break; 192 } 193 S = StmtMap->getParent(S); 194 } 195 196 if (!StmtE) 197 return; 198 199 Stmt::child_range StmtExprChild = StmtE->children(); 200 if (!StmtExprChild) 201 return; 202 CompoundStmt *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild); 203 if (!CompS) 204 return; 205 206 Stmt::child_range CompStmtChild = CompS->children(); 207 if (!CompStmtChild) 208 return; 209 DeclStmt *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild); 210 if (!DeclS) 211 return; 212 if (!DeclS->isSingleDecl()) 213 return; 214 VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()); 215 if (!VD) 216 return; 217 Expr *Init = VD->getInit(); 218 if (!Init) 219 return; 220 221 RecContainer = StmtE; 222 Rec = Init->IgnoreParenImpCasts(); 223 if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Rec)) 224 Rec = EWC->getSubExpr()->IgnoreParenImpCasts(); 225 RecRange = Rec->getSourceRange(); 226 if (SM.isMacroArgExpansion(RecRange.getBegin())) 227 RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin())); 228 if (SM.isMacroArgExpansion(RecRange.getEnd())) 229 RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd())); 230 } 231 232 void clearDiagnostics(SourceLocation loc) const { 233 Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, 234 diag::err_unavailable, 235 diag::err_unavailable_message, 236 loc); 237 } 238 239 bool isDelegateMessage(Expr *E) const { 240 if (!E) return false; 241 242 E = E->IgnoreParenCasts(); 243 244 // Also look through property-getter sugar. 245 if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) 246 E = pseudoOp->getResultExpr()->IgnoreImplicit(); 247 248 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) 249 return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); 250 251 return false; 252 } 253 254 bool isInAtFinally(Expr *E) const { 255 assert(E); 256 Stmt *S = E; 257 while (S) { 258 if (isa<ObjCAtFinallyStmt>(S)) 259 return true; 260 S = StmtMap->getParent(S); 261 } 262 263 return false; 264 } 265 266 bool isRemovable(Expr *E) const { 267 return Removables.count(E); 268 } 269 270 bool tryRemoving(Expr *E) const { 271 if (isRemovable(E)) { 272 Pass.TA.removeStmt(E); 273 return true; 274 } 275 276 Stmt *parent = StmtMap->getParent(E); 277 278 if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) 279 return tryRemoving(castE); 280 281 if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) 282 return tryRemoving(parenE); 283 284 if (BinaryOperator * 285 bopE = dyn_cast_or_null<BinaryOperator>(parent)) { 286 if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && 287 isRemovable(bopE)) { 288 Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); 289 return true; 290 } 291 } 292 293 return false; 294 } 295 296}; 297 298} // anonymous namespace 299 300void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { 301 BodyTransform<RetainReleaseDeallocRemover> trans(pass); 302 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 303} 304