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(nullptr) {
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 (auto *PID : IMD->property_impls()) {
117      if (PID->getPropertyImplementation() ==
118          ObjCPropertyImplDecl::Synthesize) {
119        ObjCPropertyDecl *PD = PID->getPropertyDecl();
120        ObjCMethodDecl *setterM = PD->getSetterMethodDecl();
121        if (!(setterM && setterM->isDefined())) {
122          ObjCPropertyDecl::PropertyAttributeKind AttrKind =
123            PD->getPropertyAttributes();
124            if (AttrKind &
125                (ObjCPropertyDecl::OBJC_PR_retain |
126                  ObjCPropertyDecl::OBJC_PR_copy   |
127                  ObjCPropertyDecl::OBJC_PR_strong))
128              SynthesizedProperties[PD] = PID;
129        }
130      }
131    }
132
133    // Now, remove all zeroing of ivars etc.
134    base::TraverseObjCMethodDecl(D);
135
136    // clear out for next method.
137    SynthesizedProperties.clear();
138    SelfD = nullptr;
139    Removables.clear();
140    return true;
141  }
142
143  bool TraverseFunctionDecl(FunctionDecl *D) { return true; }
144  bool TraverseBlockDecl(BlockDecl *block) { return true; }
145  bool TraverseBlockExpr(BlockExpr *block) { return true; }
146
147private:
148  bool isRemovable(Expr *E) const {
149    return Removables.count(E);
150  }
151
152  bool isZeroingPropIvar(Expr *E) {
153    E = E->IgnoreParens();
154    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
155      return isZeroingPropIvar(BO);
156    if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E))
157      return isZeroingPropIvar(PO);
158    return false;
159  }
160
161  bool isZeroingPropIvar(BinaryOperator *BOE) {
162    if (BOE->getOpcode() == BO_Comma)
163      return isZeroingPropIvar(BOE->getLHS()) &&
164             isZeroingPropIvar(BOE->getRHS());
165
166    if (BOE->getOpcode() != BO_Assign)
167      return false;
168
169    Expr *LHS = BOE->getLHS();
170    if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
171      ObjCIvarDecl *IVDecl = IV->getDecl();
172      if (!IVDecl->getType()->isObjCObjectPointerType())
173        return false;
174      bool IvarBacksPropertySynthesis = false;
175      for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator
176           P = SynthesizedProperties.begin(),
177           E = SynthesizedProperties.end(); P != E; ++P) {
178        ObjCPropertyImplDecl *PropImpDecl = P->second;
179        if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) {
180          IvarBacksPropertySynthesis = true;
181          break;
182        }
183      }
184      if (!IvarBacksPropertySynthesis)
185        return false;
186    }
187    else
188        return false;
189
190    return isZero(BOE->getRHS());
191  }
192
193  bool isZeroingPropIvar(PseudoObjectExpr *PO) {
194    BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm());
195    if (!BO) return false;
196    if (BO->getOpcode() != BO_Assign) return false;
197
198    ObjCPropertyRefExpr *PropRefExp =
199      dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens());
200    if (!PropRefExp) return false;
201
202    // TODO: Using implicit property decl.
203    if (PropRefExp->isImplicitProperty())
204      return false;
205
206    if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) {
207      if (!SynthesizedProperties.count(PDecl))
208        return false;
209    }
210
211    return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr());
212  }
213
214  bool isZero(Expr *E) {
215    if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull))
216      return true;
217
218    return isZeroingPropIvar(E);
219  }
220};
221
222} // anonymous namespace
223
224void trans::removeZeroOutPropsInDeallocFinalize(MigrationPass &pass) {
225  ZeroOutInDeallocRemover trans(pass);
226  trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
227}
228