Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransAutoreleasePool.cpp
35236 views
//===--- TransAutoreleasePool.cpp - Transformations to ARC mode -----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// rewriteAutoreleasePool:9//10// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.11//12// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];13// ...14// [pool release];15// ---->16// @autorelease {17// ...18// }19//20// An NSAutoreleasePool will not be touched if:21// - There is not a corresponding -release/-drain in the same scope22// - Not all references of the NSAutoreleasePool variable can be removed23// - There is a variable that is declared inside the intended @autorelease scope24// which is also used outside it.25//26//===----------------------------------------------------------------------===//2728#include "Transforms.h"29#include "Internals.h"30#include "clang/AST/ASTContext.h"31#include "clang/Basic/SourceManager.h"32#include "clang/Sema/SemaDiagnostic.h"33#include <map>3435using namespace clang;36using namespace arcmt;37using namespace trans;3839namespace {4041class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {42Decl *Dcl;43SmallVectorImpl<ObjCMessageExpr *> &Releases;4445public:46ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)47: Dcl(D), Releases(releases) { }4849bool VisitObjCMessageExpr(ObjCMessageExpr *E) {50if (!E->isInstanceMessage())51return true;52if (E->getMethodFamily() != OMF_release)53return true;54Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();55if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {56if (DE->getDecl() == Dcl)57Releases.push_back(E);58}59return true;60}61};6263}6465namespace {6667class AutoreleasePoolRewriter68: public RecursiveASTVisitor<AutoreleasePoolRewriter> {69public:70AutoreleasePoolRewriter(MigrationPass &pass)71: Body(nullptr), Pass(pass) {72PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");73DrainSel = pass.Ctx.Selectors.getNullarySelector(74&pass.Ctx.Idents.get("drain"));75}7677void transformBody(Stmt *body, Decl *ParentD) {78Body = body;79TraverseStmt(body);80}8182~AutoreleasePoolRewriter() {83SmallVector<VarDecl *, 8> VarsToHandle;8485for (std::map<VarDecl *, PoolVarInfo>::iterator86I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {87VarDecl *var = I->first;88PoolVarInfo &info = I->second;8990// Check that we can handle/rewrite all references of the pool.9192clearRefsIn(info.Dcl, info.Refs);93for (SmallVectorImpl<PoolScope>::iterator94scpI = info.Scopes.begin(),95scpE = info.Scopes.end(); scpI != scpE; ++scpI) {96PoolScope &scope = *scpI;97clearRefsIn(*scope.Begin, info.Refs);98clearRefsIn(*scope.End, info.Refs);99clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);100}101102// Even if one reference is not handled we will not do anything about that103// pool variable.104if (info.Refs.empty())105VarsToHandle.push_back(var);106}107108for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {109PoolVarInfo &info = PoolVars[VarsToHandle[i]];110111Transaction Trans(Pass.TA);112113clearUnavailableDiags(info.Dcl);114Pass.TA.removeStmt(info.Dcl);115116// Add "@autoreleasepool { }"117for (SmallVectorImpl<PoolScope>::iterator118scpI = info.Scopes.begin(),119scpE = info.Scopes.end(); scpI != scpE; ++scpI) {120PoolScope &scope = *scpI;121clearUnavailableDiags(*scope.Begin);122clearUnavailableDiags(*scope.End);123if (scope.IsFollowedBySimpleReturnStmt) {124// Include the return in the scope.125Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");126Pass.TA.removeStmt(*scope.End);127Stmt::child_iterator retI = scope.End;128++retI;129SourceLocation afterSemi =130findLocationAfterSemi((*retI)->getEndLoc(), Pass.Ctx);131assert(afterSemi.isValid() &&132"Didn't we check before setting IsFollowedBySimpleReturnStmt "133"to true?");134Pass.TA.insertAfterToken(afterSemi, "\n}");135Pass.TA.increaseIndentation(136SourceRange(scope.getIndentedRange().getBegin(),137(*retI)->getEndLoc()),138scope.CompoundParent->getBeginLoc());139} else {140Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");141Pass.TA.replaceStmt(*scope.End, "}");142Pass.TA.increaseIndentation(scope.getIndentedRange(),143scope.CompoundParent->getBeginLoc());144}145}146147// Remove rest of pool var references.148for (SmallVectorImpl<PoolScope>::iterator149scpI = info.Scopes.begin(),150scpE = info.Scopes.end(); scpI != scpE; ++scpI) {151PoolScope &scope = *scpI;152for (SmallVectorImpl<ObjCMessageExpr *>::iterator153relI = scope.Releases.begin(),154relE = scope.Releases.end(); relI != relE; ++relI) {155clearUnavailableDiags(*relI);156Pass.TA.removeStmt(*relI);157}158}159}160}161162bool VisitCompoundStmt(CompoundStmt *S) {163SmallVector<PoolScope, 4> Scopes;164165for (Stmt::child_iterator166I = S->body_begin(), E = S->body_end(); I != E; ++I) {167Stmt *child = getEssential(*I);168if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {169if (DclS->isSingleDecl()) {170if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {171if (isNSAutoreleasePool(VD->getType())) {172PoolVarInfo &info = PoolVars[VD];173info.Dcl = DclS;174collectRefs(VD, S, info.Refs);175// Does this statement follow the pattern:176// NSAutoreleasePool * pool = [NSAutoreleasePool new];177if (isPoolCreation(VD->getInit())) {178Scopes.push_back(PoolScope());179Scopes.back().PoolVar = VD;180Scopes.back().CompoundParent = S;181Scopes.back().Begin = I;182}183}184}185}186} else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {187if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {188if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {189// Does this statement follow the pattern:190// pool = [NSAutoreleasePool new];191if (isNSAutoreleasePool(VD->getType()) &&192isPoolCreation(bop->getRHS())) {193Scopes.push_back(PoolScope());194Scopes.back().PoolVar = VD;195Scopes.back().CompoundParent = S;196Scopes.back().Begin = I;197}198}199}200}201202if (Scopes.empty())203continue;204205if (isPoolDrain(Scopes.back().PoolVar, child)) {206PoolScope &scope = Scopes.back();207scope.End = I;208handlePoolScope(scope, S);209Scopes.pop_back();210}211}212return true;213}214215private:216void clearUnavailableDiags(Stmt *S) {217if (S)218Pass.TA.clearDiagnostic(diag::err_unavailable,219diag::err_unavailable_message,220S->getSourceRange());221}222223struct PoolScope {224VarDecl *PoolVar;225CompoundStmt *CompoundParent;226Stmt::child_iterator Begin;227Stmt::child_iterator End;228bool IsFollowedBySimpleReturnStmt;229SmallVector<ObjCMessageExpr *, 4> Releases;230231PoolScope()232: PoolVar(nullptr), CompoundParent(nullptr),233IsFollowedBySimpleReturnStmt(false) {}234235SourceRange getIndentedRange() const {236Stmt::child_iterator rangeS = Begin;237++rangeS;238if (rangeS == End)239return SourceRange();240Stmt::child_iterator rangeE = Begin;241for (Stmt::child_iterator I = rangeS; I != End; ++I)242++rangeE;243return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc());244}245};246247class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{248ASTContext &Ctx;249SourceRange ScopeRange;250SourceLocation &referenceLoc, &declarationLoc;251252public:253NameReferenceChecker(ASTContext &ctx, PoolScope &scope,254SourceLocation &referenceLoc,255SourceLocation &declarationLoc)256: Ctx(ctx), referenceLoc(referenceLoc),257declarationLoc(declarationLoc) {258ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(),259(*scope.End)->getBeginLoc());260}261262bool VisitDeclRefExpr(DeclRefExpr *E) {263return checkRef(E->getLocation(), E->getDecl()->getLocation());264}265266bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {267return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());268}269270bool VisitTagTypeLoc(TagTypeLoc TL) {271return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());272}273274private:275bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {276if (isInScope(declLoc)) {277referenceLoc = refLoc;278declarationLoc = declLoc;279return false;280}281return true;282}283284bool isInScope(SourceLocation loc) {285if (loc.isInvalid())286return false;287288SourceManager &SM = Ctx.getSourceManager();289if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))290return false;291return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());292}293};294295void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {296// Check that all names declared inside the scope are not used297// outside the scope.298{299bool nameUsedOutsideScope = false;300SourceLocation referenceLoc, declarationLoc;301Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();302++SI;303// Check if the autoreleasepool scope is followed by a simple return304// statement, in which case we will include the return in the scope.305if (SI != SE)306if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))307if ((retS->getRetValue() == nullptr ||308isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&309findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()) {310scope.IsFollowedBySimpleReturnStmt = true;311++SI; // the return will be included in scope, don't check it.312}313314for (; SI != SE; ++SI) {315nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,316referenceLoc,317declarationLoc).TraverseStmt(*SI);318if (nameUsedOutsideScope)319break;320}321322// If not all references were cleared it means some variables/typenames/etc323// declared inside the pool scope are used outside of it.324// We won't try to rewrite the pool.325if (nameUsedOutsideScope) {326Pass.TA.reportError("a name is referenced outside the "327"NSAutoreleasePool scope that it was declared in", referenceLoc);328Pass.TA.reportNote("name declared here", declarationLoc);329Pass.TA.reportNote("intended @autoreleasepool scope begins here",330(*scope.Begin)->getBeginLoc());331Pass.TA.reportNote("intended @autoreleasepool scope ends here",332(*scope.End)->getBeginLoc());333return;334}335}336337// Collect all releases of the pool; they will be removed.338{339ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);340Stmt::child_iterator I = scope.Begin;341++I;342for (; I != scope.End; ++I)343releaseColl.TraverseStmt(*I);344}345346PoolVars[scope.PoolVar].Scopes.push_back(scope);347}348349bool isPoolCreation(Expr *E) {350if (!E) return false;351E = getEssential(E);352ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);353if (!ME) return false;354if (ME->getMethodFamily() == OMF_new &&355ME->getReceiverKind() == ObjCMessageExpr::Class &&356isNSAutoreleasePool(ME->getReceiverInterface()))357return true;358if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&359ME->getMethodFamily() == OMF_init) {360Expr *rec = getEssential(ME->getInstanceReceiver());361if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {362if (recME->getMethodFamily() == OMF_alloc &&363recME->getReceiverKind() == ObjCMessageExpr::Class &&364isNSAutoreleasePool(recME->getReceiverInterface()))365return true;366}367}368369return false;370}371372bool isPoolDrain(VarDecl *poolVar, Stmt *S) {373if (!S) return false;374S = getEssential(S);375ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);376if (!ME) return false;377if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {378Expr *rec = getEssential(ME->getInstanceReceiver());379if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))380if (dref->getDecl() == poolVar)381return ME->getMethodFamily() == OMF_release ||382ME->getSelector() == DrainSel;383}384385return false;386}387388bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {389return IDecl && IDecl->getIdentifier() == PoolII;390}391392bool isNSAutoreleasePool(QualType Ty) {393QualType pointee = Ty->getPointeeType();394if (pointee.isNull())395return false;396if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())397return isNSAutoreleasePool(interT->getDecl());398return false;399}400401static Expr *getEssential(Expr *E) {402return cast<Expr>(getEssential((Stmt*)E));403}404static Stmt *getEssential(Stmt *S) {405if (FullExpr *FE = dyn_cast<FullExpr>(S))406S = FE->getSubExpr();407if (Expr *E = dyn_cast<Expr>(S))408S = E->IgnoreParenCasts();409return S;410}411412Stmt *Body;413MigrationPass &Pass;414415IdentifierInfo *PoolII;416Selector DrainSel;417418struct PoolVarInfo {419DeclStmt *Dcl = nullptr;420ExprSet Refs;421SmallVector<PoolScope, 2> Scopes;422423PoolVarInfo() = default;424};425426std::map<VarDecl *, PoolVarInfo> PoolVars;427};428429} // anonymous namespace430431void trans::rewriteAutoreleasePool(MigrationPass &pass) {432BodyTransform<AutoreleasePoolRewriter> trans(pass);433trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());434}435436437