diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt index 90c2bbf54e..1a64b12721 100644 --- a/lib/ARCMigrate/CMakeLists.txt +++ b/lib/ARCMigrate/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangARCMigrate TransEmptyStatementsAndDealloc.cpp TransformActions.cpp Transforms.cpp + TransGCAttrs.cpp TransGCCalls.cpp TransProperties.cpp TransRetainReleaseDealloc.cpp diff --git a/lib/ARCMigrate/TransGCAttrs.cpp b/lib/ARCMigrate/TransGCAttrs.cpp new file mode 100644 index 0000000000..60573a44e8 --- /dev/null +++ b/lib/ARCMigrate/TransGCAttrs.cpp @@ -0,0 +1,191 @@ +//===--- TransGCAttrs.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. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/Support/SaveAndRestore.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; + +namespace { + +/// \brief Collects all the places where GC attributes __strong/__weak occur. +class GCAttrsCollector : public RecursiveASTVisitor { + MigrationContext &MigrateCtx; + bool FullyMigratable; + + typedef RecursiveASTVisitor base; +public: + explicit GCAttrsCollector(MigrationContext &ctx) + : MigrateCtx(ctx), FullyMigratable(false) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { + handleAttr(TL); + return true; + } + + bool TraverseDecl(Decl *D) { + if (!D || D->isImplicit()) + return true; + + bool migratable = isMigratable(D); + SaveAndRestore Save(FullyMigratable, migratable); + + if (DeclaratorDecl *DD = dyn_cast(D)) + lookForAttribute(DD, DD->getTypeSourceInfo()); + else if (ObjCPropertyDecl *PropD = dyn_cast(D)) + lookForAttribute(PropD, PropD->getTypeSourceInfo()); + return base::TraverseDecl(D); + } + + void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { + if (!TInfo) + return; + TypeLoc TL = TInfo->getTypeLoc(); + while (TL) { + if (const AttributedTypeLoc *Attr = dyn_cast(&TL)) { + if (handleAttr(*Attr, D)) + break; + TL = Attr->getModifiedLoc(); + } if (const ArrayTypeLoc *Arr = dyn_cast(&TL)) { + TL = Arr->getElementLoc(); + } else if (const PointerTypeLoc *PT = dyn_cast(&TL)) { + TL = PT->getPointeeLoc(); + } else if (const ReferenceTypeLoc *RT = dyn_cast(&TL)) + TL = RT->getPointeeLoc(); + else + break; + } + } + + bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) { + if (TL.getAttrKind() != AttributedType::attr_objc_ownership) + return false; + + SourceLocation Loc = TL.getAttrNameLoc(); + unsigned RawLoc = Loc.getRawEncoding(); + if (MigrateCtx.AttrSet.count(RawLoc)) + return true; + + ASTContext &Ctx = MigrateCtx.Pass.Ctx; + SourceManager &SM = Ctx.getSourceManager(); + llvm::SmallString<32> Buf; + bool Invalid = false; + StringRef Spell = Lexer::getSpelling( + SM.getSpellingLoc(TL.getAttrEnumOperandLoc()), + Buf, SM, Ctx.getLangOptions(), &Invalid); + if (Invalid) + return false; + MigrationContext::GCAttrOccurrence::AttrKind Kind; + if (Spell == "strong") + Kind = MigrationContext::GCAttrOccurrence::Strong; + else if (Spell == "weak") + Kind = MigrationContext::GCAttrOccurrence::Weak; + else + return false; + + MigrateCtx.AttrSet.insert(RawLoc); + MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); + MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); + + Attr.Kind = Kind; + Attr.Loc = Loc; + Attr.ModifiedType = TL.getModifiedLoc().getType(); + Attr.Dcl = D; + Attr.FullyMigratable = FullyMigratable; + return true; + } + + bool isMigratable(Decl *D) { + if (isa(D)) + return false; + + if (isInMainFile(D)) + return true; + + if (FunctionDecl *FD = dyn_cast(D)) + return FD->hasBody(); + + if (ObjCContainerDecl *ContD = dyn_cast(D)) { + if (ObjCInterfaceDecl *ID = dyn_cast(ContD)) + return ID->getImplementation() != 0; + if (ObjCCategoryDecl *CD = dyn_cast(ContD)) + return CD->getImplementation() != 0; + if (isa(ContD)) + return true; + return false; + } + + if (CXXRecordDecl *RD = dyn_cast(D)) { + for (CXXRecordDecl::method_iterator + MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) { + if ((*MI)->isOutOfLine()) + return true; + } + return false; + } + + return isMigratable(cast(D->getDeclContext())); + } + + bool isInMainFile(Decl *D) { + if (!D) + return false; + + for (Decl::redecl_iterator + I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I) + if (!isInMainFile((*I)->getLocation())) + return false; + + return true; + } + + bool isInMainFile(SourceLocation Loc) { + if (Loc.isInvalid()) + return false; + + SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); + return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); + } +}; + +} // anonymous namespace + +void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { + GCAttrsCollector(MigrateCtx).TraverseDecl( + MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); +#if 0 + llvm::errs() << "\n################\n"; + for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { + MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; + llvm::errs() << "KIND: " + << (Attr.Kind == MigrationContext::GCAttrOccurrence::Strong ? "strong" + : "weak"); + llvm::errs() << "\nLOC: "; + Attr.Loc.dump(MigrateCtx.Pass.Ctx.getSourceManager()); + llvm::errs() << "\nTYPE: "; + Attr.ModifiedType.dump(); + if (Attr.Dcl) { + llvm::errs() << "DECL:\n"; + Attr.Dcl->dump(); + } else { + llvm::errs() << "DECL: NONE"; + } + llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; + llvm::errs() << "\n----------------\n"; + } + llvm::errs() << "\n################\n"; +#endif +} diff --git a/lib/ARCMigrate/TransGCCalls.cpp b/lib/ARCMigrate/TransGCCalls.cpp index 94e965df18..7c0819a677 100644 --- a/lib/ARCMigrate/TransGCCalls.cpp +++ b/lib/ARCMigrate/TransGCCalls.cpp @@ -27,13 +27,15 @@ class GCCollectableCallsChecker : public: GCCollectableCallsChecker(MigrationContext &ctx, ParentMap &map) : MigrateCtx(ctx), PMap(map) { - IdentifierTable &Ids = MigrateCtx.getPass().Ctx.Idents; + IdentifierTable &Ids = MigrateCtx.Pass.Ctx.Idents; NSMakeCollectableII = &Ids.get("NSMakeCollectable"); CFMakeCollectableII = &Ids.get("CFMakeCollectable"); } + bool shouldWalkTypesOfTypeLocs() const { return false; } + bool VisitCallExpr(CallExpr *E) { - TransformActions &TA = MigrateCtx.getPass().TA; + TransformActions &TA = MigrateCtx.Pass.TA; if (MigrateCtx.isGCOwnedNonObjC(E->getType())) { TA.reportError("call returns pointer to GC managed memory; " diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index 6f9166fb98..85eb156935 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -528,6 +528,6 @@ public: void PropertyRewriteTraverser::traverseObjCImplementation( ObjCImplementationContext &ImplCtx) { - PropertiesRewriter(ImplCtx.getMigrationContext().getPass()) + PropertiesRewriter(ImplCtx.getMigrationContext().Pass) .doTransform(ImplCtx.getImplementationDecl()); } diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp index 5ea454a917..c82f075bca 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -302,6 +302,8 @@ class ASTTransform : public RecursiveASTVisitor { public: ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { } + bool shouldWalkTypesOfTypeLocs() const { return false; } + bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { ObjCImplementationContext ImplCtx(MigrateCtx, D); for (MigrationContext::traverser_iterator @@ -355,6 +357,10 @@ bool MigrationContext::isGCOwnedNonObjC(QualType T) { } void MigrationContext::traverse(TranslationUnitDecl *TU) { + for (traverser_iterator + I = traversers_begin(), E = traversers_end(); I != E; ++I) + (*I)->traverseTU(*this); + ASTTransform(*this).TraverseDecl(TU); } @@ -367,6 +373,7 @@ static void traverseAST(MigrationPass &pass) { if (pass.isGCMigration()) { MigrateCtx.addTraverser(new GCCollectableCallsTraverser); + MigrateCtx.addTraverser(new GCAttrsTraverser()); } MigrateCtx.addTraverser(new PropertyRewriteTraverser()); diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h index 4ff35a5daf..540426b81a 100644 --- a/lib/ARCMigrate/Transforms.h +++ b/lib/ARCMigrate/Transforms.h @@ -79,14 +79,26 @@ public: }; class MigrationContext { - MigrationPass &Pass; std::vector Traversers; public: + MigrationPass &Pass; + + struct GCAttrOccurrence { + enum AttrKind { Weak, Strong } Kind; + SourceLocation Loc; + QualType ModifiedType; + Decl *Dcl; + /// \brief true if the attribute is owned, e.g. it is in a body and not just + /// in an interface. + bool FullyMigratable; + }; + std::vector GCAttrs; + + llvm::DenseSet AttrSet; + explicit MigrationContext(MigrationPass &pass) : Pass(pass) {} ~MigrationContext(); - - MigrationPass &getPass() { return Pass; } typedef std::vector::iterator traverser_iterator; traverser_iterator traversers_begin() { return Traversers.begin(); } @@ -108,6 +120,11 @@ public: // GC transformations +class GCAttrsTraverser : public ASTTraverser { +public: + virtual void traverseTU(MigrationContext &MigrateCtx); +}; + class GCCollectableCallsTraverser : public ASTTraverser { public: virtual void traverseBody(BodyContext &BodyCtx);