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