llvm-project

Форк
0
/
TransAutoreleasePool.cpp 
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

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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.