• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- TransProperties.cpp - Tranformations to ARC mode -----------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // rewriteProperties:
11 //
12 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
13 //   are missing one.
14 // - Migrates properties from (retain) to (strong) and (assign) to
15 //   (unsafe_unretained/weak).
16 // - If a property is synthesized, adds the ownership specifier in the ivar
17 //   backing the property.
18 //
19 //  @interface Foo : NSObject {
20 //      NSObject *x;
21 //  }
22 //  @property (assign) id x;
23 //  @end
24 // ---->
25 //  @interface Foo : NSObject {
26 //      NSObject *__weak x;
27 //  }
28 //  @property (weak) id x;
29 //  @end
30 //
31 //===----------------------------------------------------------------------===//
32 
33 #include "Transforms.h"
34 #include "Internals.h"
35 #include "clang/Sema/SemaDiagnostic.h"
36 #include "clang/Basic/SourceManager.h"
37 #include "clang/Lex/Lexer.h"
38 #include <map>
39 
40 using namespace clang;
41 using namespace arcmt;
42 using namespace trans;
43 using llvm::StringRef;
44 
45 namespace {
46 
47 class PropertiesRewriter {
48   MigrationPass &Pass;
49 
50   struct PropData {
51     ObjCPropertyDecl *PropD;
52     ObjCIvarDecl *IvarD;
53     ObjCPropertyImplDecl *ImplD;
54 
PropData__anon16a169420111::PropertiesRewriter::PropData55     PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
56   };
57 
58   typedef llvm::SmallVector<PropData, 2> PropsTy;
59   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
60   AtPropDeclsTy AtProps;
61 
62 public:
PropertiesRewriter(MigrationPass & pass)63   PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
64 
doTransform(ObjCImplementationDecl * D)65   void doTransform(ObjCImplementationDecl *D) {
66     ObjCInterfaceDecl *iface = D->getClassInterface();
67     if (!iface)
68       return;
69 
70     for (ObjCInterfaceDecl::prop_iterator
71            propI = iface->prop_begin(),
72            propE = iface->prop_end(); propI != propE; ++propI) {
73       if (propI->getAtLoc().isInvalid())
74         continue;
75       PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
76       props.push_back(*propI);
77     }
78 
79     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
80         prop_impl_iterator;
81     for (prop_impl_iterator
82            I = prop_impl_iterator(D->decls_begin()),
83            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
84       ObjCPropertyImplDecl *implD = *I;
85       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
86         continue;
87       ObjCPropertyDecl *propD = implD->getPropertyDecl();
88       if (!propD || propD->isInvalidDecl())
89         continue;
90       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
91       if (!ivarD || ivarD->isInvalidDecl())
92         continue;
93       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
94       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
95       if (findAtLoc == AtProps.end())
96         continue;
97 
98       PropsTy &props = findAtLoc->second;
99       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
100         if (I->PropD == propD) {
101           I->IvarD = ivarD;
102           I->ImplD = implD;
103           break;
104         }
105       }
106     }
107 
108     for (AtPropDeclsTy::iterator
109            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
110       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
111       PropsTy &props = I->second;
112       QualType ty = getPropertyType(props);
113       if (!ty->isObjCRetainableType())
114         continue;
115       if (hasIvarWithExplicitOwnership(props))
116         continue;
117 
118       Transaction Trans(Pass.TA);
119       rewriteProperty(props, atLoc);
120     }
121   }
122 
123 private:
rewriteProperty(PropsTy & props,SourceLocation atLoc) const124   void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
125     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
126 
127     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
128                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
129                      ObjCPropertyDecl::OBJC_PR_strong |
130                      ObjCPropertyDecl::OBJC_PR_weak))
131       return;
132 
133     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
134       rewriteAttribute("retain", "strong", atLoc);
135       return;
136     }
137 
138     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
139       return rewriteAssign(props, atLoc);
140 
141     return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
142   }
143 
rewriteAssign(PropsTy & props,SourceLocation atLoc) const144   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
145     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
146 
147     bool rewroteAttr = rewriteAttribute("assign",
148                                      canUseWeak ? "weak" : "unsafe_unretained",
149                                          atLoc);
150     if (!rewroteAttr)
151       canUseWeak = false;
152 
153     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
154       if (isUserDeclared(I->IvarD))
155         Pass.TA.insert(I->IvarD->getLocation(),
156                        canUseWeak ? "__weak " : "__unsafe_unretained ");
157       if (I->ImplD)
158         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
159                                 I->ImplD->getLocation());
160     }
161   }
162 
maybeAddWeakOrUnsafeUnretainedAttr(PropsTy & props,SourceLocation atLoc) const163   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
164                                           SourceLocation atLoc) const {
165     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
166     if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
167         hasNoBackingIvars(props))
168       return;
169 
170     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
171     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
172                                   atLoc);
173     if (!addedAttr)
174       canUseWeak = false;
175 
176     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
177       if (isUserDeclared(I->IvarD))
178         Pass.TA.insert(I->IvarD->getLocation(),
179                        canUseWeak ? "__weak " : "__unsafe_unretained ");
180       if (I->ImplD) {
181         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
182                                 I->ImplD->getLocation());
183         Pass.TA.clearDiagnostic(
184                            diag::err_arc_objc_property_default_assign_on_object,
185                            I->ImplD->getLocation());
186       }
187     }
188   }
189 
rewriteAttribute(llvm::StringRef fromAttr,llvm::StringRef toAttr,SourceLocation atLoc) const190   bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr,
191                         SourceLocation atLoc) const {
192     if (atLoc.isMacroID())
193       return false;
194 
195     SourceManager &SM = Pass.Ctx.getSourceManager();
196 
197     // Break down the source location.
198     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
199 
200     // Try to load the file buffer.
201     bool invalidTemp = false;
202     llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
203     if (invalidTemp)
204       return false;
205 
206     const char *tokenBegin = file.data() + locInfo.second;
207 
208     // Lex from the start of the given location.
209     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
210                 Pass.Ctx.getLangOptions(),
211                 file.begin(), tokenBegin, file.end());
212     Token tok;
213     lexer.LexFromRawLexer(tok);
214     if (tok.isNot(tok::at)) return false;
215     lexer.LexFromRawLexer(tok);
216     if (tok.isNot(tok::raw_identifier)) return false;
217     if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
218           != "property")
219       return false;
220     lexer.LexFromRawLexer(tok);
221     if (tok.isNot(tok::l_paren)) return false;
222 
223     lexer.LexFromRawLexer(tok);
224     if (tok.is(tok::r_paren))
225       return false;
226 
227     while (1) {
228       if (tok.isNot(tok::raw_identifier)) return false;
229       llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength());
230       if (ident == fromAttr) {
231         Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
232         return true;
233       }
234 
235       do {
236         lexer.LexFromRawLexer(tok);
237       } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
238       if (tok.is(tok::r_paren))
239         break;
240       lexer.LexFromRawLexer(tok);
241     }
242 
243     return false;
244   }
245 
addAttribute(llvm::StringRef attr,SourceLocation atLoc) const246   bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const {
247     if (atLoc.isMacroID())
248       return false;
249 
250     SourceManager &SM = Pass.Ctx.getSourceManager();
251 
252     // Break down the source location.
253     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
254 
255     // Try to load the file buffer.
256     bool invalidTemp = false;
257     llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
258     if (invalidTemp)
259       return false;
260 
261     const char *tokenBegin = file.data() + locInfo.second;
262 
263     // Lex from the start of the given location.
264     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
265                 Pass.Ctx.getLangOptions(),
266                 file.begin(), tokenBegin, file.end());
267     Token tok;
268     lexer.LexFromRawLexer(tok);
269     if (tok.isNot(tok::at)) return false;
270     lexer.LexFromRawLexer(tok);
271     if (tok.isNot(tok::raw_identifier)) return false;
272     if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
273           != "property")
274       return false;
275     lexer.LexFromRawLexer(tok);
276 
277     if (tok.isNot(tok::l_paren)) {
278       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
279       return true;
280     }
281 
282     lexer.LexFromRawLexer(tok);
283     if (tok.is(tok::r_paren)) {
284       Pass.TA.insert(tok.getLocation(), attr);
285       return true;
286     }
287 
288     if (tok.isNot(tok::raw_identifier)) return false;
289 
290     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
291     return true;
292   }
293 
hasIvarWithExplicitOwnership(PropsTy & props) const294   bool hasIvarWithExplicitOwnership(PropsTy &props) const {
295     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
296       if (isUserDeclared(I->IvarD)) {
297         if (isa<AttributedType>(I->IvarD->getType()))
298           return true;
299         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
300               != Qualifiers::OCL_Strong)
301           return true;
302       }
303     }
304 
305     return false;
306   }
307 
hasNoBackingIvars(PropsTy & props) const308   bool hasNoBackingIvars(PropsTy &props) const {
309     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
310       if (I->IvarD)
311         return false;
312 
313     return true;
314   }
315 
isUserDeclared(ObjCIvarDecl * ivarD) const316   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
317     return ivarD && !ivarD->getSynthesize();
318   }
319 
getPropertyType(PropsTy & props) const320   QualType getPropertyType(PropsTy &props) const {
321     assert(!props.empty());
322     QualType ty = props[0].PropD->getType();
323 
324 #ifndef NDEBUG
325     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
326       assert(ty == I->PropD->getType());
327 #endif
328 
329     return ty;
330   }
331 
332   ObjCPropertyDecl::PropertyAttributeKind
getPropertyAttrs(PropsTy & props) const333   getPropertyAttrs(PropsTy &props) const {
334     assert(!props.empty());
335     ObjCPropertyDecl::PropertyAttributeKind
336       attrs = props[0].PropD->getPropertyAttributesAsWritten();
337 
338 #ifndef NDEBUG
339     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
340       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
341 #endif
342 
343     return attrs;
344   }
345 };
346 
347 class ImplementationChecker :
348                              public RecursiveASTVisitor<ImplementationChecker> {
349   MigrationPass &Pass;
350 
351 public:
ImplementationChecker(MigrationPass & pass)352   ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
353 
TraverseObjCImplementationDecl(ObjCImplementationDecl * D)354   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
355     PropertiesRewriter(Pass).doTransform(D);
356     return true;
357   }
358 };
359 
360 } // anonymous namespace
361 
rewriteProperties(MigrationPass & pass)362 void trans::rewriteProperties(MigrationPass &pass) {
363   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
364 }
365