1//===--- TransZeroOutPropsInDealloc.cpp - Transformations 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// removeZeroOutPropsInDealloc:
11//
12// Removes zero'ing out "strong" @synthesized properties in a -dealloc method.
13//
14//===----------------------------------------------------------------------===//
15
16#include "Transforms.h"
17#include "Internals.h"
18#include "clang/AST/ASTContext.h"
19
20using namespace clang;
21using namespace arcmt;
22using namespace trans;
23
24namespace {
25
26class ZeroOutInDeallocRemover :
27                           public RecursiveASTVisitor<ZeroOutInDeallocRemover> {
28  typedef RecursiveASTVisitor<ZeroOutInDeallocRemover> base;
29
30  MigrationPass &Pass;
31
32  llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties;
33  ImplicitParamDecl *SelfD;
34  ExprSet Removables;
35  Selector FinalizeSel;
36
37public:
38  ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(0) {
39    FinalizeSel =
40        Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
41  }
42
43  bool VisitObjCMessageExpr(ObjCMessageExpr *ME) {
44    ASTContext &Ctx = Pass.Ctx;
45    TransformActions &TA = Pass.TA;
46
47    if (ME->getReceiverKind() != ObjCMessageExpr::Instance)
48      return true;
49    Expr *receiver = ME->getInstanceReceiver();
50    if (!receiver)
51      return true;
52
53    DeclRefExpr *refE = dyn_cast<DeclRefExpr>(receiver->IgnoreParenCasts());
54    if (!refE || refE->getDecl() != SelfD)
55      return true;
56
57    bool BackedBySynthesizeSetter = false;
58    for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
59         P = SynthesizedProperties.begin(),
60         E = SynthesizedProperties.end(); P != E; ++P) {
61      ObjCPropertyDecl *PropDecl = P->first;
62      if (PropDecl->getSetterName() == ME->getSelector()) {
63        BackedBySynthesizeSetter = true;
64        break;
65      }
66    }
67    if (!BackedBySynthesizeSetter)
68      return true;
69
70    // Remove the setter message if RHS is null
71    Transaction Trans(TA);
72    Expr *RHS = ME->getArg(0);
73    bool RHSIsNull =
74      RHS->isNullPointerConstant(Ctx,
75                                 Expr::NPC_ValueDependentIsNull);
76    if (RHSIsNull && isRemovable(ME))
77      TA.removeStmt(ME);
78
79    return true;
80  }
81
82  bool VisitPseudoObjectExpr(PseudoObjectExpr *POE) {
83    if (isZeroingPropIvar(POE) && isRemovable(POE)) {
84      Transaction Trans(Pass.TA);
85      Pass.TA.removeStmt(POE);
86    }
87
88    return true;
89  }
90
91  bool VisitBinaryOperator(BinaryOperator *BOE) {
92    if (isZeroingPropIvar(BOE) && isRemovable(BOE)) {
93      Transaction Trans(Pass.TA);
94      Pass.TA.removeStmt(BOE);
95    }
96
97    return true;
98  }
99
100  bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
101    if (D->getMethodFamily() != OMF_dealloc &&
102        !(D->isInstanceMethod() && D->getSelector() == FinalizeSel))
103      return true;
104    if (!D->hasBody())
105      return true;
106
107    ObjCImplDecl *IMD = dyn_cast<ObjCImplDecl>(D->getDeclContext());
108    if (!IMD)
109      return true;
110
111    SelfD = D->getSelfDecl();
112    collectRemovables(D->getBody(), Removables);
113
114    // For a 'dealloc' method use, find all property implementations in
115    // this class implementation.
116    for (ObjCImplDecl::propimpl_iterator
117           I = IMD->propimpl_begin(), EI = IMD->propimpl_end(); I != EI; ++I) {
118        ObjCPropertyImplDecl *PID = *I;
119        if (PID->getPropertyImplementation() ==
120            ObjCPropertyImplDecl::Synthesize) {
121          ObjCPropertyDecl *PD = PID->getPropertyDecl();
122          ObjCMethodDecl *setterM = PD->getSetterMethodDecl();
123          if (!(setterM && setterM->isDefined())) {
124            ObjCPropertyDecl::PropertyAttributeKind AttrKind =
125              PD->getPropertyAttributes();
126              if (AttrKind &
127                  (ObjCPropertyDecl::OBJC_PR_retain |
128                   ObjCPropertyDecl::OBJC_PR_copy   |
129                   ObjCPropertyDecl::OBJC_PR_strong))
130                SynthesizedProperties[PD] = PID;
131          }
132        }
133    }
134
135    // Now, remove all zeroing of ivars etc.
136    base::TraverseObjCMethodDecl(D);
137
138    // clear out for next method.
139    SynthesizedProperties.clear();
140    SelfD = 0;
141    Removables.clear();
142    return true;
143  }
144
145  bool TraverseFunctionDecl(FunctionDecl *D) { return true; }
146  bool TraverseBlockDecl(BlockDecl *block) { return true; }
147  bool TraverseBlockExpr(BlockExpr *block) { return true; }
148
149private:
150  bool isRemovable(Expr *E) const {
151    return Removables.count(E);
152  }
153
154  bool isZeroingPropIvar(Expr *E) {
155    E = E->IgnoreParens();
156    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
157      return isZeroingPropIvar(BO);
158    if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E))
159      return isZeroingPropIvar(PO);
160    return false;
161  }
162
163  bool isZeroingPropIvar(BinaryOperator *BOE) {
164    if (BOE->getOpcode() == BO_Comma)
165      return isZeroingPropIvar(BOE->getLHS()) &&
166             isZeroingPropIvar(BOE->getRHS());
167
168    if (BOE->getOpcode() != BO_Assign)
169      return false;
170
171    Expr *LHS = BOE->getLHS();
172    if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
173      ObjCIvarDecl *IVDecl = IV->getDecl();
174      if (!IVDecl->getType()->isObjCObjectPointerType())
175        return false;
176      bool IvarBacksPropertySynthesis = false;
177      for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
178           P = SynthesizedProperties.begin(),
179           E = SynthesizedProperties.end(); P != E; ++P) {
180        ObjCPropertyImplDecl *PropImpDecl = P->second;
181        if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) {
182          IvarBacksPropertySynthesis = true;
183          break;
184        }
185      }
186      if (!IvarBacksPropertySynthesis)
187        return false;
188    }
189    else
190        return false;
191
192    return isZero(BOE->getRHS());
193  }
194
195  bool isZeroingPropIvar(PseudoObjectExpr *PO) {
196    BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm());
197    if (!BO) return false;
198    if (BO->getOpcode() != BO_Assign) return false;
199
200    ObjCPropertyRefExpr *PropRefExp =
201      dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens());
202    if (!PropRefExp) return false;
203
204    // TODO: Using implicit property decl.
205    if (PropRefExp->isImplicitProperty())
206      return false;
207
208    if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) {
209      if (!SynthesizedProperties.count(PDecl))
210        return false;
211    }
212
213    return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr());
214  }
215
216  bool isZero(Expr *E) {
217    if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull))
218      return true;
219
220    return isZeroingPropIvar(E);
221  }
222};
223
224} // anonymous namespace
225
226void trans::removeZeroOutPropsInDeallocFinalize(MigrationPass &pass) {
227  ZeroOutInDeallocRemover trans(pass);
228  trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
229}
230