llvm-project
435 строк · 14.2 Кб
1//===--- TransAutoreleasePool.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// rewriteAutoreleasePool:
10//
11// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope.
12//
13// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
14// ...
15// [pool release];
16// ---->
17// @autorelease {
18// ...
19// }
20//
21// An NSAutoreleasePool will not be touched if:
22// - There is not a corresponding -release/-drain in the same scope
23// - Not all references of the NSAutoreleasePool variable can be removed
24// - There is a variable that is declared inside the intended @autorelease scope
25// which is also used outside it.
26//
27//===----------------------------------------------------------------------===//
28
29#include "Transforms.h"
30#include "Internals.h"
31#include "clang/AST/ASTContext.h"
32#include "clang/Basic/SourceManager.h"
33#include "clang/Sema/SemaDiagnostic.h"
34#include <map>
35
36using namespace clang;
37using namespace arcmt;
38using namespace trans;
39
40namespace {
41
42class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> {
43Decl *Dcl;
44SmallVectorImpl<ObjCMessageExpr *> &Releases;
45
46public:
47ReleaseCollector(Decl *D, SmallVectorImpl<ObjCMessageExpr *> &releases)
48: Dcl(D), Releases(releases) { }
49
50bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
51if (!E->isInstanceMessage())
52return true;
53if (E->getMethodFamily() != OMF_release)
54return true;
55Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts();
56if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) {
57if (DE->getDecl() == Dcl)
58Releases.push_back(E);
59}
60return true;
61}
62};
63
64}
65
66namespace {
67
68class AutoreleasePoolRewriter
69: public RecursiveASTVisitor<AutoreleasePoolRewriter> {
70public:
71AutoreleasePoolRewriter(MigrationPass &pass)
72: Body(nullptr), Pass(pass) {
73PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool");
74DrainSel = pass.Ctx.Selectors.getNullarySelector(
75&pass.Ctx.Idents.get("drain"));
76}
77
78void transformBody(Stmt *body, Decl *ParentD) {
79Body = body;
80TraverseStmt(body);
81}
82
83~AutoreleasePoolRewriter() {
84SmallVector<VarDecl *, 8> VarsToHandle;
85
86for (std::map<VarDecl *, PoolVarInfo>::iterator
87I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) {
88VarDecl *var = I->first;
89PoolVarInfo &info = I->second;
90
91// Check that we can handle/rewrite all references of the pool.
92
93clearRefsIn(info.Dcl, info.Refs);
94for (SmallVectorImpl<PoolScope>::iterator
95scpI = info.Scopes.begin(),
96scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
97PoolScope &scope = *scpI;
98clearRefsIn(*scope.Begin, info.Refs);
99clearRefsIn(*scope.End, info.Refs);
100clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs);
101}
102
103// Even if one reference is not handled we will not do anything about that
104// pool variable.
105if (info.Refs.empty())
106VarsToHandle.push_back(var);
107}
108
109for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) {
110PoolVarInfo &info = PoolVars[VarsToHandle[i]];
111
112Transaction Trans(Pass.TA);
113
114clearUnavailableDiags(info.Dcl);
115Pass.TA.removeStmt(info.Dcl);
116
117// Add "@autoreleasepool { }"
118for (SmallVectorImpl<PoolScope>::iterator
119scpI = info.Scopes.begin(),
120scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
121PoolScope &scope = *scpI;
122clearUnavailableDiags(*scope.Begin);
123clearUnavailableDiags(*scope.End);
124if (scope.IsFollowedBySimpleReturnStmt) {
125// Include the return in the scope.
126Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
127Pass.TA.removeStmt(*scope.End);
128Stmt::child_iterator retI = scope.End;
129++retI;
130SourceLocation afterSemi =
131findLocationAfterSemi((*retI)->getEndLoc(), Pass.Ctx);
132assert(afterSemi.isValid() &&
133"Didn't we check before setting IsFollowedBySimpleReturnStmt "
134"to true?");
135Pass.TA.insertAfterToken(afterSemi, "\n}");
136Pass.TA.increaseIndentation(
137SourceRange(scope.getIndentedRange().getBegin(),
138(*retI)->getEndLoc()),
139scope.CompoundParent->getBeginLoc());
140} else {
141Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {");
142Pass.TA.replaceStmt(*scope.End, "}");
143Pass.TA.increaseIndentation(scope.getIndentedRange(),
144scope.CompoundParent->getBeginLoc());
145}
146}
147
148// Remove rest of pool var references.
149for (SmallVectorImpl<PoolScope>::iterator
150scpI = info.Scopes.begin(),
151scpE = info.Scopes.end(); scpI != scpE; ++scpI) {
152PoolScope &scope = *scpI;
153for (SmallVectorImpl<ObjCMessageExpr *>::iterator
154relI = scope.Releases.begin(),
155relE = scope.Releases.end(); relI != relE; ++relI) {
156clearUnavailableDiags(*relI);
157Pass.TA.removeStmt(*relI);
158}
159}
160}
161}
162
163bool VisitCompoundStmt(CompoundStmt *S) {
164SmallVector<PoolScope, 4> Scopes;
165
166for (Stmt::child_iterator
167I = S->body_begin(), E = S->body_end(); I != E; ++I) {
168Stmt *child = getEssential(*I);
169if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) {
170if (DclS->isSingleDecl()) {
171if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) {
172if (isNSAutoreleasePool(VD->getType())) {
173PoolVarInfo &info = PoolVars[VD];
174info.Dcl = DclS;
175collectRefs(VD, S, info.Refs);
176// Does this statement follow the pattern:
177// NSAutoreleasePool * pool = [NSAutoreleasePool new];
178if (isPoolCreation(VD->getInit())) {
179Scopes.push_back(PoolScope());
180Scopes.back().PoolVar = VD;
181Scopes.back().CompoundParent = S;
182Scopes.back().Begin = I;
183}
184}
185}
186}
187} else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) {
188if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) {
189if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) {
190// Does this statement follow the pattern:
191// pool = [NSAutoreleasePool new];
192if (isNSAutoreleasePool(VD->getType()) &&
193isPoolCreation(bop->getRHS())) {
194Scopes.push_back(PoolScope());
195Scopes.back().PoolVar = VD;
196Scopes.back().CompoundParent = S;
197Scopes.back().Begin = I;
198}
199}
200}
201}
202
203if (Scopes.empty())
204continue;
205
206if (isPoolDrain(Scopes.back().PoolVar, child)) {
207PoolScope &scope = Scopes.back();
208scope.End = I;
209handlePoolScope(scope, S);
210Scopes.pop_back();
211}
212}
213return true;
214}
215
216private:
217void clearUnavailableDiags(Stmt *S) {
218if (S)
219Pass.TA.clearDiagnostic(diag::err_unavailable,
220diag::err_unavailable_message,
221S->getSourceRange());
222}
223
224struct PoolScope {
225VarDecl *PoolVar;
226CompoundStmt *CompoundParent;
227Stmt::child_iterator Begin;
228Stmt::child_iterator End;
229bool IsFollowedBySimpleReturnStmt;
230SmallVector<ObjCMessageExpr *, 4> Releases;
231
232PoolScope()
233: PoolVar(nullptr), CompoundParent(nullptr),
234IsFollowedBySimpleReturnStmt(false) {}
235
236SourceRange getIndentedRange() const {
237Stmt::child_iterator rangeS = Begin;
238++rangeS;
239if (rangeS == End)
240return SourceRange();
241Stmt::child_iterator rangeE = Begin;
242for (Stmt::child_iterator I = rangeS; I != End; ++I)
243++rangeE;
244return SourceRange((*rangeS)->getBeginLoc(), (*rangeE)->getEndLoc());
245}
246};
247
248class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{
249ASTContext &Ctx;
250SourceRange ScopeRange;
251SourceLocation &referenceLoc, &declarationLoc;
252
253public:
254NameReferenceChecker(ASTContext &ctx, PoolScope &scope,
255SourceLocation &referenceLoc,
256SourceLocation &declarationLoc)
257: Ctx(ctx), referenceLoc(referenceLoc),
258declarationLoc(declarationLoc) {
259ScopeRange = SourceRange((*scope.Begin)->getBeginLoc(),
260(*scope.End)->getBeginLoc());
261}
262
263bool VisitDeclRefExpr(DeclRefExpr *E) {
264return checkRef(E->getLocation(), E->getDecl()->getLocation());
265}
266
267bool VisitTypedefTypeLoc(TypedefTypeLoc TL) {
268return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation());
269}
270
271bool VisitTagTypeLoc(TagTypeLoc TL) {
272return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation());
273}
274
275private:
276bool checkRef(SourceLocation refLoc, SourceLocation declLoc) {
277if (isInScope(declLoc)) {
278referenceLoc = refLoc;
279declarationLoc = declLoc;
280return false;
281}
282return true;
283}
284
285bool isInScope(SourceLocation loc) {
286if (loc.isInvalid())
287return false;
288
289SourceManager &SM = Ctx.getSourceManager();
290if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
291return false;
292return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd());
293}
294};
295
296void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) {
297// Check that all names declared inside the scope are not used
298// outside the scope.
299{
300bool nameUsedOutsideScope = false;
301SourceLocation referenceLoc, declarationLoc;
302Stmt::child_iterator SI = scope.End, SE = compoundS->body_end();
303++SI;
304// Check if the autoreleasepool scope is followed by a simple return
305// statement, in which case we will include the return in the scope.
306if (SI != SE)
307if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI))
308if ((retS->getRetValue() == nullptr ||
309isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) &&
310findLocationAfterSemi(retS->getEndLoc(), Pass.Ctx).isValid()) {
311scope.IsFollowedBySimpleReturnStmt = true;
312++SI; // the return will be included in scope, don't check it.
313}
314
315for (; SI != SE; ++SI) {
316nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope,
317referenceLoc,
318declarationLoc).TraverseStmt(*SI);
319if (nameUsedOutsideScope)
320break;
321}
322
323// If not all references were cleared it means some variables/typenames/etc
324// declared inside the pool scope are used outside of it.
325// We won't try to rewrite the pool.
326if (nameUsedOutsideScope) {
327Pass.TA.reportError("a name is referenced outside the "
328"NSAutoreleasePool scope that it was declared in", referenceLoc);
329Pass.TA.reportNote("name declared here", declarationLoc);
330Pass.TA.reportNote("intended @autoreleasepool scope begins here",
331(*scope.Begin)->getBeginLoc());
332Pass.TA.reportNote("intended @autoreleasepool scope ends here",
333(*scope.End)->getBeginLoc());
334return;
335}
336}
337
338// Collect all releases of the pool; they will be removed.
339{
340ReleaseCollector releaseColl(scope.PoolVar, scope.Releases);
341Stmt::child_iterator I = scope.Begin;
342++I;
343for (; I != scope.End; ++I)
344releaseColl.TraverseStmt(*I);
345}
346
347PoolVars[scope.PoolVar].Scopes.push_back(scope);
348}
349
350bool isPoolCreation(Expr *E) {
351if (!E) return false;
352E = getEssential(E);
353ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
354if (!ME) return false;
355if (ME->getMethodFamily() == OMF_new &&
356ME->getReceiverKind() == ObjCMessageExpr::Class &&
357isNSAutoreleasePool(ME->getReceiverInterface()))
358return true;
359if (ME->getReceiverKind() == ObjCMessageExpr::Instance &&
360ME->getMethodFamily() == OMF_init) {
361Expr *rec = getEssential(ME->getInstanceReceiver());
362if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) {
363if (recME->getMethodFamily() == OMF_alloc &&
364recME->getReceiverKind() == ObjCMessageExpr::Class &&
365isNSAutoreleasePool(recME->getReceiverInterface()))
366return true;
367}
368}
369
370return false;
371}
372
373bool isPoolDrain(VarDecl *poolVar, Stmt *S) {
374if (!S) return false;
375S = getEssential(S);
376ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
377if (!ME) return false;
378if (ME->getReceiverKind() == ObjCMessageExpr::Instance) {
379Expr *rec = getEssential(ME->getInstanceReceiver());
380if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec))
381if (dref->getDecl() == poolVar)
382return ME->getMethodFamily() == OMF_release ||
383ME->getSelector() == DrainSel;
384}
385
386return false;
387}
388
389bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) {
390return IDecl && IDecl->getIdentifier() == PoolII;
391}
392
393bool isNSAutoreleasePool(QualType Ty) {
394QualType pointee = Ty->getPointeeType();
395if (pointee.isNull())
396return false;
397if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>())
398return isNSAutoreleasePool(interT->getDecl());
399return false;
400}
401
402static Expr *getEssential(Expr *E) {
403return cast<Expr>(getEssential((Stmt*)E));
404}
405static Stmt *getEssential(Stmt *S) {
406if (FullExpr *FE = dyn_cast<FullExpr>(S))
407S = FE->getSubExpr();
408if (Expr *E = dyn_cast<Expr>(S))
409S = E->IgnoreParenCasts();
410return S;
411}
412
413Stmt *Body;
414MigrationPass &Pass;
415
416IdentifierInfo *PoolII;
417Selector DrainSel;
418
419struct PoolVarInfo {
420DeclStmt *Dcl = nullptr;
421ExprSet Refs;
422SmallVector<PoolScope, 2> Scopes;
423
424PoolVarInfo() = default;
425};
426
427std::map<VarDecl *, PoolVarInfo> PoolVars;
428};
429
430} // anonymous namespace
431
432void trans::rewriteAutoreleasePool(MigrationPass &pass) {
433BodyTransform<AutoreleasePoolRewriter> trans(pass);
434trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
435}
436