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