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