diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2011-07-17 15:40:56 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2011-07-17 15:40:56 +0000 | 
| commit | 180abc3db9ae3b4fc63cd65b15697e6ffcc8a657 (patch) | |
| tree | 2097d084eb235c0b12c0bff3445f4ec7bbaa8a12 /lib/ARCMigrate/TransRetainReleaseDealloc.cpp | |
| parent | 29cafa66ad3878dbb9f82615f19fa0bded2e443c (diff) | |
Diffstat (limited to 'lib/ARCMigrate/TransRetainReleaseDealloc.cpp')
| -rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 219 | 
1 files changed, 219 insertions, 0 deletions
| diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp new file mode 100644 index 000000000000..ed6ed0adfdf2 --- /dev/null +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -0,0 +1,219 @@ +//===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// removeRetainReleaseDealloc: +// +// Removes retain/release/autorelease/dealloc messages. +// +//  return [[foo retain] autorelease]; +// ----> +//  return foo; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class RetainReleaseDeallocRemover : +                       public RecursiveASTVisitor<RetainReleaseDeallocRemover> { +  Stmt *Body; +  MigrationPass &Pass; + +  ExprSet Removables; +  llvm::OwningPtr<ParentMap> StmtMap; + +  Selector DelegateSel; + +public: +  RetainReleaseDeallocRemover(MigrationPass &pass) +    : Body(0), Pass(pass) { +    DelegateSel = +        Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); +  } + +  void transformBody(Stmt *body) { +    Body = body; +    collectRemovables(body, Removables); +    StmtMap.reset(new ParentMap(body)); +    TraverseStmt(body); +  } + +  bool VisitObjCMessageExpr(ObjCMessageExpr *E) { +    switch (E->getMethodFamily()) { +    default: +      return true; +    case OMF_autorelease: +      if (isRemovable(E)) { +        // An unused autorelease is badness. If we remove it the receiver +        // will likely die immediately while previously it was kept alive +        // by the autorelease pool. This is bad practice in general, leave it +        // and emit an error to force the user to restructure his code. +        Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " +            "message; its receiver may be destroyed immediately", +            E->getLocStart(), E->getSourceRange()); +        return true; +      } +      // Pass through. +    case OMF_retain: +    case OMF_release: +      if (E->getReceiverKind() == ObjCMessageExpr::Instance) +        if (Expr *rec = E->getInstanceReceiver()) { +          rec = rec->IgnoreParenImpCasts(); +          if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && +              (E->getMethodFamily() != OMF_retain || isRemovable(E))) { +            std::string err = "it is not safe to remove '"; +            err += E->getSelector().getAsString() + "' message on " +                "an __unsafe_unretained type"; +            Pass.TA.reportError(err, rec->getLocStart()); +            return true; +          } + +          if (isGlobalVar(rec) && +              (E->getMethodFamily() != OMF_retain || isRemovable(E))) { +            std::string err = "it is not safe to remove '"; +            err += E->getSelector().getAsString() + "' message on " +                "a global variable"; +            Pass.TA.reportError(err, rec->getLocStart()); +            return true; +          } + +          if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { +            Pass.TA.reportError("it is not safe to remove 'retain' " +                "message on the result of a 'delegate' message; " +                "the object that was passed to 'setDelegate:' may not be " +                "properly retained", rec->getLocStart()); +            return true; +          } +        } +    case OMF_dealloc: +      break; +    } + +    switch (E->getReceiverKind()) { +    default: +      return true; +    case ObjCMessageExpr::SuperInstance: { +      Transaction Trans(Pass.TA); +      clearDiagnostics(E->getSuperLoc()); +      if (tryRemoving(E)) +        return true; +      Pass.TA.replace(E->getSourceRange(), "self"); +      return true; +    } +    case ObjCMessageExpr::Instance: +      break; +    } + +    Expr *rec = E->getInstanceReceiver(); +    if (!rec) return true; + +    Transaction Trans(Pass.TA); +    clearDiagnostics(rec->getExprLoc()); + +    if (E->getMethodFamily() == OMF_release && +        isRemovable(E) && isInAtFinally(E)) { +      // Change the -release to "receiver = nil" in a finally to avoid a leak +      // when an exception is thrown. +      Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); +      if (Pass.Ctx.Idents.get("nil").hasMacroDefinition()) +        Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil"); +      else +        Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0"); +      return true; +    } + +    if (!hasSideEffects(E, Pass.Ctx)) { +      if (tryRemoving(E)) +        return true; +    } +    Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + +    return true; +  } + +private: +  void clearDiagnostics(SourceLocation loc) const { +    Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, +                            diag::err_unavailable, +                            diag::err_unavailable_message, +                            loc); +  } + +  bool isDelegateMessage(Expr *E) const { +    if (!E) return false; + +    E = E->IgnoreParenCasts(); +    if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) +      return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); + +    if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) +      return propE->getGetterSelector() == DelegateSel; + +    return false; +  } + +  bool isInAtFinally(Expr *E) const { +    assert(E); +    Stmt *S = E; +    while (S) { +      if (isa<ObjCAtFinallyStmt>(S)) +        return true; +      S = StmtMap->getParent(S); +    } + +    return false; +  } + +  bool isRemovable(Expr *E) const { +    return Removables.count(E); +  } +   +  bool tryRemoving(Expr *E) const { +    if (isRemovable(E)) { +      Pass.TA.removeStmt(E); +      return true; +    } + +    Stmt *parent = StmtMap->getParent(E); + +    if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) +      return tryRemoving(castE); + +    if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) +      return tryRemoving(parenE); + +    if (BinaryOperator * +          bopE = dyn_cast_or_null<BinaryOperator>(parent)) { +      if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && +          isRemovable(bopE)) { +        Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); +        return true; +      } +    } + +    return false; +  } + +}; + +} // anonymous namespace + +void trans::removeRetainReleaseDealloc(MigrationPass &pass) { +  BodyTransform<RetainReleaseDeallocRemover> trans(pass); +  trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} | 
