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