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