398 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			398 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // rewriteProperties:
 | |
| //
 | |
| // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
 | |
| //   are missing one.
 | |
| // - Migrates properties from (retain) to (strong) and (assign) to
 | |
| //   (unsafe_unretained/weak).
 | |
| // - If a property is synthesized, adds the ownership specifier in the ivar
 | |
| //   backing the property.
 | |
| //
 | |
| //  @interface Foo : NSObject {
 | |
| //      NSObject *x;
 | |
| //  }
 | |
| //  @property (assign) id x;
 | |
| //  @end
 | |
| // ---->
 | |
| //  @interface Foo : NSObject {
 | |
| //      NSObject *__weak x;
 | |
| //  }
 | |
| //  @property (weak) id x;
 | |
| //  @end
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "Transforms.h"
 | |
| #include "Internals.h"
 | |
| #include "clang/Basic/SourceManager.h"
 | |
| #include "clang/Lex/Lexer.h"
 | |
| #include "clang/Sema/SemaDiagnostic.h"
 | |
| #include <map>
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace arcmt;
 | |
| using namespace trans;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class PropertiesRewriter {
 | |
|   MigrationContext &MigrateCtx;
 | |
|   MigrationPass &Pass;
 | |
|   ObjCImplementationDecl *CurImplD;
 | |
|   
 | |
|   enum PropActionKind {
 | |
|     PropAction_None,
 | |
|     PropAction_RetainReplacedWithStrong,
 | |
|     PropAction_AssignRemoved,
 | |
|     PropAction_AssignRewritten,
 | |
|     PropAction_MaybeAddWeakOrUnsafe
 | |
|   };
 | |
| 
 | |
|   struct PropData {
 | |
|     ObjCPropertyDecl *PropD;
 | |
|     ObjCIvarDecl *IvarD;
 | |
|     ObjCPropertyImplDecl *ImplD;
 | |
| 
 | |
|     PropData(ObjCPropertyDecl *propD)
 | |
|       : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
 | |
|   };
 | |
| 
 | |
|   typedef SmallVector<PropData, 2> PropsTy;
 | |
|   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
 | |
|   AtPropDeclsTy AtProps;
 | |
|   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
 | |
| 
 | |
| public:
 | |
|   explicit PropertiesRewriter(MigrationContext &MigrateCtx)
 | |
|     : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
 | |
| 
 | |
|   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
 | |
|                                 AtPropDeclsTy *PrevAtProps = nullptr) {
 | |
|     for (auto *Prop : D->properties()) {
 | |
|       if (Prop->getAtLoc().isInvalid())
 | |
|         continue;
 | |
|       unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
 | |
|       if (PrevAtProps)
 | |
|         if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
 | |
|           continue;
 | |
|       PropsTy &props = AtProps[RawLoc];
 | |
|       props.push_back(Prop);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void doTransform(ObjCImplementationDecl *D) {
 | |
|     CurImplD = D;
 | |
|     ObjCInterfaceDecl *iface = D->getClassInterface();
 | |
|     if (!iface)
 | |
|       return;
 | |
| 
 | |
|     collectProperties(iface, AtProps);
 | |
| 
 | |
|     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
 | |
|         prop_impl_iterator;
 | |
|     for (prop_impl_iterator
 | |
|            I = prop_impl_iterator(D->decls_begin()),
 | |
|            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
 | |
|       ObjCPropertyImplDecl *implD = *I;
 | |
|       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
 | |
|         continue;
 | |
|       ObjCPropertyDecl *propD = implD->getPropertyDecl();
 | |
|       if (!propD || propD->isInvalidDecl())
 | |
|         continue;
 | |
|       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
 | |
|       if (!ivarD || ivarD->isInvalidDecl())
 | |
|         continue;
 | |
|       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
 | |
|       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
 | |
|       if (findAtLoc == AtProps.end())
 | |
|         continue;
 | |
|       
 | |
|       PropsTy &props = findAtLoc->second;
 | |
|       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|         if (I->PropD == propD) {
 | |
|           I->IvarD = ivarD;
 | |
|           I->ImplD = implD;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     for (AtPropDeclsTy::iterator
 | |
|            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
 | |
|       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
 | |
|       PropsTy &props = I->second;
 | |
|       if (!getPropertyType(props)->isObjCRetainableType())
 | |
|         continue;
 | |
|       if (hasIvarWithExplicitARCOwnership(props))
 | |
|         continue;
 | |
|       
 | |
|       Transaction Trans(Pass.TA);
 | |
|       rewriteProperty(props, atLoc);
 | |
|     }
 | |
| 
 | |
|     AtPropDeclsTy AtExtProps;
 | |
|     // Look through extensions.
 | |
|     for (auto *Ext : iface->visible_extensions())
 | |
|       collectProperties(Ext, AtExtProps, &AtProps);
 | |
| 
 | |
|     for (AtPropDeclsTy::iterator
 | |
|            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
 | |
|       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
 | |
|       PropsTy &props = I->second;
 | |
|       Transaction Trans(Pass.TA);
 | |
|       doActionForExtensionProp(props, atLoc);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   void doPropAction(PropActionKind kind,
 | |
|                     PropsTy &props, SourceLocation atLoc,
 | |
|                     bool markAction = true) {
 | |
|     if (markAction)
 | |
|       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
 | |
|         ActionOnProp[I->PropD->getIdentifier()] = kind;
 | |
| 
 | |
|     switch (kind) {
 | |
|     case PropAction_None:
 | |
|       return;
 | |
|     case PropAction_RetainReplacedWithStrong: {
 | |
|       StringRef toAttr = "strong";
 | |
|       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
 | |
|       return;
 | |
|     }
 | |
|     case PropAction_AssignRemoved:
 | |
|       return removeAssignForDefaultStrong(props, atLoc);
 | |
|     case PropAction_AssignRewritten:
 | |
|       return rewriteAssign(props, atLoc);
 | |
|     case PropAction_MaybeAddWeakOrUnsafe:
 | |
|       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
 | |
|     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
 | |
|     I = ActionOnProp.find(props[0].PropD->getIdentifier());
 | |
|     if (I == ActionOnProp.end())
 | |
|       return;
 | |
| 
 | |
|     doPropAction(I->second, props, atLoc, false);
 | |
|   }
 | |
| 
 | |
|   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
 | |
|     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
 | |
|     
 | |
|     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
 | |
|                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
 | |
|                      ObjCPropertyDecl::OBJC_PR_strong |
 | |
|                      ObjCPropertyDecl::OBJC_PR_weak))
 | |
|       return;
 | |
| 
 | |
|     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
 | |
|       // strong is the default.
 | |
|       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
 | |
|     }
 | |
| 
 | |
|     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
 | |
| 
 | |
|     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
 | |
|       if (HasIvarAssignedAPlusOneObject)
 | |
|         return doPropAction(PropAction_AssignRemoved, props, atLoc);
 | |
|       return doPropAction(PropAction_AssignRewritten, props, atLoc);
 | |
|     }
 | |
| 
 | |
|     if (HasIvarAssignedAPlusOneObject ||
 | |
|         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
 | |
|       return; // 'strong' by default.
 | |
| 
 | |
|     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
 | |
|   }
 | |
| 
 | |
|   void removeAssignForDefaultStrong(PropsTy &props,
 | |
|                                     SourceLocation atLoc) const {
 | |
|     removeAttribute("retain", atLoc);
 | |
|     if (!removeAttribute("assign", atLoc))
 | |
|       return;
 | |
| 
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|       if (I->ImplD)
 | |
|         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
 | |
|                                 diag::err_arc_assign_property_ownership,
 | |
|                                 diag::err_arc_inconsistent_property_ownership,
 | |
|                                 I->IvarD->getLocation());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
 | |
|     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
 | |
|                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
 | |
|     const char *toWhich = 
 | |
|       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
 | |
|       (canUseWeak ? "weak" : "unsafe_unretained");
 | |
| 
 | |
|     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
 | |
|     if (!rewroteAttr)
 | |
|       canUseWeak = false;
 | |
| 
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|       if (isUserDeclared(I->IvarD)) {
 | |
|         if (I->IvarD &&
 | |
|             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
 | |
|           const char *toWhich = 
 | |
|             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
 | |
|               (canUseWeak ? "__weak " : "__unsafe_unretained ");
 | |
|           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
 | |
|         }
 | |
|       }
 | |
|       if (I->ImplD)
 | |
|         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
 | |
|                                 diag::err_arc_assign_property_ownership,
 | |
|                                 diag::err_arc_inconsistent_property_ownership,
 | |
|                                 I->IvarD->getLocation());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
 | |
|                                           SourceLocation atLoc) const {
 | |
|     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
 | |
|                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
 | |
| 
 | |
|     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
 | |
|                                   atLoc);
 | |
|     if (!addedAttr)
 | |
|       canUseWeak = false;
 | |
| 
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|       if (isUserDeclared(I->IvarD)) {
 | |
|         if (I->IvarD &&
 | |
|             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
 | |
|           Pass.TA.insert(I->IvarD->getLocation(),
 | |
|                          canUseWeak ? "__weak " : "__unsafe_unretained ");
 | |
|       }
 | |
|       if (I->ImplD) {
 | |
|         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
 | |
|                                 diag::err_arc_assign_property_ownership,
 | |
|                                 diag::err_arc_inconsistent_property_ownership,
 | |
|                                 I->IvarD->getLocation());
 | |
|         Pass.TA.clearDiagnostic(
 | |
|                            diag::err_arc_objc_property_default_assign_on_object,
 | |
|                            I->ImplD->getLocation());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
 | |
|     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
 | |
|   }
 | |
| 
 | |
|   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
 | |
|                         SourceLocation atLoc) const {
 | |
|     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
 | |
|   }
 | |
| 
 | |
|   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
 | |
|     return MigrateCtx.addPropertyAttribute(attr, atLoc);
 | |
|   }
 | |
| 
 | |
|   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
 | |
|     ObjCIvarDecl *Ivar;
 | |
|   public:
 | |
|     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
 | |
| 
 | |
|     bool VisitBinAssign(BinaryOperator *E) {
 | |
|       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
 | |
|       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
 | |
|         if (RE->getDecl() != Ivar)
 | |
|           return true;
 | |
| 
 | |
|         if (isPlusOneAssign(E))
 | |
|           return false;
 | |
|       }
 | |
| 
 | |
|       return true;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|       PlusOneAssign oneAssign(I->IvarD);
 | |
|       bool notFound = oneAssign.TraverseDecl(CurImplD);
 | |
|       if (!notFound)
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
 | |
|     if (Pass.isGCMigration())
 | |
|       return false;
 | |
| 
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
 | |
|       if (isUserDeclared(I->IvarD)) {
 | |
|         if (isa<AttributedType>(I->IvarD->getType()))
 | |
|           return true;
 | |
|         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
 | |
|               != Qualifiers::OCL_Strong)
 | |
|           return true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return false;    
 | |
|   }
 | |
| 
 | |
|   // \brief Returns true if all declarations in the @property have GC __weak.
 | |
|   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
 | |
|     if (!Pass.isGCMigration())
 | |
|       return false;
 | |
|     if (props.empty())
 | |
|       return false;
 | |
|     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
 | |
|   }
 | |
| 
 | |
|   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
 | |
|     return ivarD && !ivarD->getSynthesize();
 | |
|   }
 | |
| 
 | |
|   QualType getPropertyType(PropsTy &props) const {
 | |
|     assert(!props.empty());
 | |
|     QualType ty = props[0].PropD->getType().getUnqualifiedType();
 | |
| 
 | |
| #ifndef NDEBUG
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
 | |
|       assert(ty == I->PropD->getType().getUnqualifiedType());
 | |
| #endif
 | |
| 
 | |
|     return ty;
 | |
|   }
 | |
| 
 | |
|   ObjCPropertyDecl::PropertyAttributeKind
 | |
|   getPropertyAttrs(PropsTy &props) const {
 | |
|     assert(!props.empty());
 | |
|     ObjCPropertyDecl::PropertyAttributeKind
 | |
|       attrs = props[0].PropD->getPropertyAttributesAsWritten();
 | |
| 
 | |
| #ifndef NDEBUG
 | |
|     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
 | |
|       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
 | |
| #endif
 | |
| 
 | |
|     return attrs;
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| void PropertyRewriteTraverser::traverseObjCImplementation(
 | |
|                                            ObjCImplementationContext &ImplCtx) {
 | |
|   PropertiesRewriter(ImplCtx.getMigrationContext())
 | |
|                                   .doTransform(ImplCtx.getImplementationDecl());
 | |
| }
 |