1*0b57cec5SDimitry Andric //===--- TransBlockObjCVariable.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 // rewriteBlockObjCVariable: 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric // Adding __block to an obj-c variable could be either because the variable 12*0b57cec5SDimitry Andric // is used for output storage or the user wanted to break a retain cycle. 13*0b57cec5SDimitry Andric // This transformation checks whether a reference of the variable for the block 14*0b57cec5SDimitry Andric // is actually needed (it is assigned to or its address is taken) or not. 15*0b57cec5SDimitry Andric // If the reference is not needed it will assume __block was added to break a 16*0b57cec5SDimitry Andric // cycle so it will remove '__block' and add __weak/__unsafe_unretained. 17*0b57cec5SDimitry Andric // e.g 18*0b57cec5SDimitry Andric // 19*0b57cec5SDimitry Andric // __block Foo *x; 20*0b57cec5SDimitry Andric // bar(^ { [x cake]; }); 21*0b57cec5SDimitry Andric // ----> 22*0b57cec5SDimitry Andric // __weak Foo *x; 23*0b57cec5SDimitry Andric // bar(^ { [x cake]; }); 24*0b57cec5SDimitry Andric // 25*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 26*0b57cec5SDimitry Andric 27*0b57cec5SDimitry Andric #include "Transforms.h" 28*0b57cec5SDimitry Andric #include "Internals.h" 29*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h" 30*0b57cec5SDimitry Andric #include "clang/AST/Attr.h" 31*0b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric using namespace clang; 34*0b57cec5SDimitry Andric using namespace arcmt; 35*0b57cec5SDimitry Andric using namespace trans; 36*0b57cec5SDimitry Andric 37*0b57cec5SDimitry Andric namespace { 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric class RootBlockObjCVarRewriter : 40*0b57cec5SDimitry Andric public RecursiveASTVisitor<RootBlockObjCVarRewriter> { 41*0b57cec5SDimitry Andric llvm::DenseSet<VarDecl *> &VarsToChange; 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> { 44*0b57cec5SDimitry Andric VarDecl *Var; 45*0b57cec5SDimitry Andric 46*0b57cec5SDimitry Andric typedef RecursiveASTVisitor<BlockVarChecker> base; 47*0b57cec5SDimitry Andric public: BlockVarChecker(VarDecl * var)48*0b57cec5SDimitry Andric BlockVarChecker(VarDecl *var) : Var(var) { } 49*0b57cec5SDimitry Andric TraverseImplicitCastExpr(ImplicitCastExpr * castE)50*0b57cec5SDimitry Andric bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) { 51*0b57cec5SDimitry Andric if (DeclRefExpr * 52*0b57cec5SDimitry Andric ref = dyn_cast<DeclRefExpr>(castE->getSubExpr())) { 53*0b57cec5SDimitry Andric if (ref->getDecl() == Var) { 54*0b57cec5SDimitry Andric if (castE->getCastKind() == CK_LValueToRValue) 55*0b57cec5SDimitry Andric return true; // Using the value of the variable. 56*0b57cec5SDimitry Andric if (castE->getCastKind() == CK_NoOp && castE->isLValue() && 57*0b57cec5SDimitry Andric Var->getASTContext().getLangOpts().CPlusPlus) 58*0b57cec5SDimitry Andric return true; // Binding to const C++ reference. 59*0b57cec5SDimitry Andric } 60*0b57cec5SDimitry Andric } 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric return base::TraverseImplicitCastExpr(castE); 63*0b57cec5SDimitry Andric } 64*0b57cec5SDimitry Andric VisitDeclRefExpr(DeclRefExpr * E)65*0b57cec5SDimitry Andric bool VisitDeclRefExpr(DeclRefExpr *E) { 66*0b57cec5SDimitry Andric if (E->getDecl() == Var) 67*0b57cec5SDimitry Andric return false; // The reference of the variable, and not just its value, 68*0b57cec5SDimitry Andric // is needed. 69*0b57cec5SDimitry Andric return true; 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric }; 72*0b57cec5SDimitry Andric 73*0b57cec5SDimitry Andric public: RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl * > & VarsToChange)74*0b57cec5SDimitry Andric RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange) 75*0b57cec5SDimitry Andric : VarsToChange(VarsToChange) { } 76*0b57cec5SDimitry Andric VisitBlockDecl(BlockDecl * block)77*0b57cec5SDimitry Andric bool VisitBlockDecl(BlockDecl *block) { 78*0b57cec5SDimitry Andric SmallVector<VarDecl *, 4> BlockVars; 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric for (const auto &I : block->captures()) { 81*0b57cec5SDimitry Andric VarDecl *var = I.getVariable(); 82*0b57cec5SDimitry Andric if (I.isByRef() && 83*0b57cec5SDimitry Andric var->getType()->isObjCObjectPointerType() && 84*0b57cec5SDimitry Andric isImplicitStrong(var->getType())) { 85*0b57cec5SDimitry Andric BlockVars.push_back(var); 86*0b57cec5SDimitry Andric } 87*0b57cec5SDimitry Andric } 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) { 90*0b57cec5SDimitry Andric VarDecl *var = BlockVars[i]; 91*0b57cec5SDimitry Andric 92*0b57cec5SDimitry Andric BlockVarChecker checker(var); 93*0b57cec5SDimitry Andric bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody()); 94*0b57cec5SDimitry Andric if (onlyValueOfVarIsNeeded) 95*0b57cec5SDimitry Andric VarsToChange.insert(var); 96*0b57cec5SDimitry Andric else 97*0b57cec5SDimitry Andric VarsToChange.erase(var); 98*0b57cec5SDimitry Andric } 99*0b57cec5SDimitry Andric 100*0b57cec5SDimitry Andric return true; 101*0b57cec5SDimitry Andric } 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric private: isImplicitStrong(QualType ty)104*0b57cec5SDimitry Andric bool isImplicitStrong(QualType ty) { 105*0b57cec5SDimitry Andric if (isa<AttributedType>(ty.getTypePtr())) 106*0b57cec5SDimitry Andric return false; 107*0b57cec5SDimitry Andric return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong; 108*0b57cec5SDimitry Andric } 109*0b57cec5SDimitry Andric }; 110*0b57cec5SDimitry Andric 111*0b57cec5SDimitry Andric class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> { 112*0b57cec5SDimitry Andric llvm::DenseSet<VarDecl *> &VarsToChange; 113*0b57cec5SDimitry Andric 114*0b57cec5SDimitry Andric public: BlockObjCVarRewriter(llvm::DenseSet<VarDecl * > & VarsToChange)115*0b57cec5SDimitry Andric BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange) 116*0b57cec5SDimitry Andric : VarsToChange(VarsToChange) { } 117*0b57cec5SDimitry Andric TraverseBlockDecl(BlockDecl * block)118*0b57cec5SDimitry Andric bool TraverseBlockDecl(BlockDecl *block) { 119*0b57cec5SDimitry Andric RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block); 120*0b57cec5SDimitry Andric return true; 121*0b57cec5SDimitry Andric } 122*0b57cec5SDimitry Andric }; 123*0b57cec5SDimitry Andric 124*0b57cec5SDimitry Andric } // anonymous namespace 125*0b57cec5SDimitry Andric traverseBody(BodyContext & BodyCtx)126*0b57cec5SDimitry Andricvoid BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) { 127*0b57cec5SDimitry Andric MigrationPass &Pass = BodyCtx.getMigrationContext().Pass; 128*0b57cec5SDimitry Andric llvm::DenseSet<VarDecl *> VarsToChange; 129*0b57cec5SDimitry Andric 130*0b57cec5SDimitry Andric BlockObjCVarRewriter trans(VarsToChange); 131*0b57cec5SDimitry Andric trans.TraverseStmt(BodyCtx.getTopStmt()); 132*0b57cec5SDimitry Andric 133*0b57cec5SDimitry Andric for (llvm::DenseSet<VarDecl *>::iterator 134*0b57cec5SDimitry Andric I = VarsToChange.begin(), E = VarsToChange.end(); I != E; ++I) { 135*0b57cec5SDimitry Andric VarDecl *var = *I; 136*0b57cec5SDimitry Andric BlocksAttr *attr = var->getAttr<BlocksAttr>(); 137*0b57cec5SDimitry Andric if(!attr) 138*0b57cec5SDimitry Andric continue; 139*0b57cec5SDimitry Andric bool useWeak = canApplyWeak(Pass.Ctx, var->getType()); 140*0b57cec5SDimitry Andric SourceManager &SM = Pass.Ctx.getSourceManager(); 141*0b57cec5SDimitry Andric Transaction Trans(Pass.TA); 142*0b57cec5SDimitry Andric Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()), 143*0b57cec5SDimitry Andric "__block", 144*0b57cec5SDimitry Andric useWeak ? "__weak" : "__unsafe_unretained"); 145*0b57cec5SDimitry Andric } 146*0b57cec5SDimitry Andric } 147