diff options
author | Eli Friedman <eli.friedman@gmail.com> | 2009-05-18 22:50:54 +0000 |
---|---|---|
committer | Eli Friedman <eli.friedman@gmail.com> | 2009-05-18 22:50:54 +0000 |
commit | 39d7c4d2d980651c4263b67cf0ab61dec34cd76b (patch) | |
tree | 7c84e3a5626a8919ada8deea9e796ff53ff92c22 /lib/Frontend/RewriteObjC.cpp | |
parent | c6d656e2b0491f224ddd48507627b51d630d749a (diff) |
Move ASTConsumers.h to include/clang/Frontend, and move the associated
.cpp files to lib/Frontend. (As proposed on cfe-dev.)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72060 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Frontend/RewriteObjC.cpp')
-rw-r--r-- | lib/Frontend/RewriteObjC.cpp | 4691 |
1 files changed, 4691 insertions, 0 deletions
diff --git a/lib/Frontend/RewriteObjC.cpp b/lib/Frontend/RewriteObjC.cpp new file mode 100644 index 0000000000..86f3037326 --- /dev/null +++ b/lib/Frontend/RewriteObjC.cpp @@ -0,0 +1,4691 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteObjC : public ASTConsumer { + Rewriter Rewrite; + Diagnostic &Diags; + const LangOptions &LangOpts; + unsigned RewriteFailedDiag; + unsigned TryFinallyContainsReturnDiag; + + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + SourceLocation LastIncLoc; + + llvm::SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; + llvm::SmallVector<ObjCCategoryImplDecl *, 8> CategoryImplementation; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs; + llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCForwardDecls; + llvm::DenseMap<ObjCMethodDecl*, std::string> MethodInternalNames; + llvm::SmallVector<Stmt *, 32> Stmts; + llvm::SmallVector<int, 8> ObjCBcLabelNo; + // Remember all the @protocol(<expr>) expressions. + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ProtocolExprDecls; + + unsigned NumObjCStringLiterals; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperContructorFunctionDecl; + + // ObjC string constant support. + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + TypeDecl *ProtocolTypeDecl; + QualType getProtocolType(); + + // Needed for header files being rewritten + bool IsHeader; + + std::string InFileName; + llvm::raw_ostream* OutFile; + + bool SilenceRewriteMacroWarning; + + std::string Preamble; + + // Block expressions. + llvm::SmallVector<BlockExpr *, 32> Blocks; + llvm::SmallVector<BlockDeclRefExpr *, 32> BlockDeclRefs; + llvm::DenseMap<BlockDeclRefExpr *, CallExpr *> BlockCallExprs; + + // Block related declarations. + llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDecls; + llvm::SmallPtrSet<ValueDecl *, 8> ImportedBlockDecls; + + llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; + + // This maps a property to it's assignment statement. + llvm::DenseMap<ObjCPropertyRefExpr *, BinaryOperator *> PropSetters; + // This maps a property to it's synthesied message expression. + // This allows us to rewrite chained getters (e.g. o.a.b.c). + llvm::DenseMap<ObjCPropertyRefExpr *, Stmt *> PropGetters; + + // This maps an original source AST to it's rewritten form. This allows + // us to avoid rewriting the same node twice (which is very uncommon). + // This is needed to support some of the exotic property rewriting. + llvm::DenseMap<Stmt *, Stmt *> ReplacedNodes; + + FunctionDecl *CurFunctionDef; + VarDecl *GlobalVarDecl; + + bool DisableReplaceStmt; + + static const int OBJC_ABI_VERSION =7 ; + public: + virtual void Initialize(ASTContext &context); + + // Top Level Driver code. + virtual void HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) + HandleTopLevelSingleDecl(*I); + } + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteObjC(std::string inFile, llvm::raw_ostream *OS, + Diagnostic &D, const LangOptions &LOpts, + bool silenceMacroWarn); + + ~RewriteObjC() {} + + virtual void HandleTranslationUnit(ASTContext &C); + + void ReplaceStmt(Stmt *Old, Stmt *New) { + Stmt *ReplacingStmt = ReplacedNodes[Old]; + + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; // Used when rewriting the assignment of a property setter. + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceStmt(Old, New)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + // Measaure the old text. + int Size = Rewrite.getRangeSize(SrcRange); + if (Size == -1) { + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + return; + } + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + New->printPretty(S); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, &Str[0], Str.size())) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, StrData, StrLen, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void RemoveText(SourceLocation Loc, unsigned StrLen) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.RemoveText(Loc, StrLen) || SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + const char *NewStr, unsigned NewLength) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, NewStr, NewLength) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewritePrologue(SourceLocation Loc); + void RewriteInclude(); + void RewriteTabs(); + void RewriteForwardClassDecl(ObjCClassDecl *Dcl); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(ObjCForwardProtocolDecl *Dcl); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + bool needToScanForQualifiers(QualType T); + ObjCInterfaceDecl *isSuperReceiver(Expr *recExpr); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + void CollectPropertySetters(Stmt *S); + + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, SourceLocation OrigStart); + Stmt *RewritePropertyGetter(ObjCPropertyRefExpr *PropRefExpr); + Stmt *RewritePropertySetter(BinaryOperator *BinOp, Expr *newStmt, + SourceRange SrcRange); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + void WarnAboutReturnGotoContinueOrBreakStmts(Stmt *S); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCCatchStmt(ObjCAtCatchStmt *S); + Stmt *RewriteObjCFinallyStmt(ObjCAtFinallyStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + Expr **args, unsigned nargs); + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void SynthCountByEnumWithState(std::string &buf); + + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperContructorFunctionDecl(); + + // Metadata emission. + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + + template<typename MethodIterator> + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + const char *prefix, + const char *ClassName, + std::string &Result); + + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + const char *prefix, + const char *ClassName, + std::string &Result); + void RewriteObjCProtocolListMetaData(const ObjCList<ObjCProtocolDecl> &Prots, + const char *prefix, + const char *ClassName, + std::string &Result); + void SynthesizeObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + void SynthesizeIvarOffsetComputation(ObjCImplementationDecl *IDecl, + ObjCIvarDecl *ivar, + std::string &Result); + void RewriteImplementations(); + void SynthesizeMetaDataIntoBuffer(std::string &Result); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + // Block specific rewrite rules. + void RewriteBlockCall(CallExpr *Exp); + void RewriteBlockPointerDecl(NamedDecl *VD); + Stmt *RewriteBlockDeclRefExpr(BlockDeclRefExpr *VD); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + const char *funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + const char *funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + bool hasCopyDisposeHelpers); + Stmt *SynthesizeBlockCall(CallExpr *Exp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + const char *FunName); + + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockCallExprs(Stmt *S); + void GetBlockDeclRefExprs(Stmt *S); + + // We avoid calling Type::isBlockPointerType(), since it operates on the + // canonical type. We only care if the top-level type is a closure pointer. + bool isTopLevelBlockPointerType(QualType T) { + return isa<BlockPointerType>(T); + } + + // FIXME: This predicate seems like it would be useful to add to ASTContext. + bool isObjCType(QualType T) { + if (!LangOpts.ObjC1 && !LangOpts.ObjC2) + return false; + + QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); + + if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || + OCT == Context->getCanonicalType(Context->getObjCClassType())) + return true; + + if (const PointerType *PT = OCT->getAsPointerType()) { + if (isa<ObjCInterfaceType>(PT->getPointeeType()) || + isa<ObjCQualifiedIdType>(PT->getPointeeType())) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + void RewriteCastExpr(CStyleCastExpr *CE); + + FunctionDecl *SynthBlockInitFunctionDecl(const char *name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for(unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + }; +} + +void RewriteObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (FunctionProtoType *fproto = dyn_cast<FunctionProtoType>(funcType)) { + for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), + E = fproto->arg_type_end(); I && (I != E); ++I) + if (isTopLevelBlockPointerType(*I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAsPointerType(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); +} + +static bool IsHeaderFile(const std::string &Filename) { + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + // no file extension + return false; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + return Ext == "h" || Ext == "hh" || Ext == "H"; +} + +RewriteObjC::RewriteObjC(std::string inFile, llvm::raw_ostream* OS, + Diagnostic &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), + SilenceRewriteMacroWarning(silenceMacroWarn) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + TryFinallyContainsReturnDiag = Diags.getCustomDiagID(Diagnostic::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +ASTConsumer *clang::CreateObjCRewriter(const std::string& InFile, + llvm::raw_ostream* OS, + Diagnostic &Diags, + const LangOptions &LOpts, + bool SilenceRewriteMacroWarning) { + return new RewriteObjC(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); +} + +void RewriteObjC::Initialize(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = 0; + MsgSendSuperFunctionDecl = 0; + MsgSendStretFunctionDecl = 0; + MsgSendSuperStretFunctionDecl = 0; + MsgSendFpretFunctionDecl = 0; + GetClassFunctionDecl = 0; + GetMetaClassFunctionDecl = 0; + SelGetUidFunctionDecl = 0; + CFStringFunctionDecl = 0; + ConstantStringClassReference = 0; + NSStringRecord = 0; + CurMethodDef = 0; + CurFunctionDef = 0; + GlobalVarDecl = 0; + SuperStructDecl = 0; + ProtocolTypeDecl = 0; + ConstantStringDecl = 0; + BcLabelCount = 0; + SuperContructorFunctionDecl = 0; + NumObjCStringLiterals = 0; + PropParentMap = 0; + CurrentBody = 0; + DisableReplaceStmt = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOptions()); + + // declaring objc_selector outside the parameter list removes a silly + // scope related warning... + if (IsHeader) + Preamble = "#pragma once\n"; + Preamble += "struct objc_selector; struct objc_class;\n"; + Preamble += "struct __rw_objc_super { struct objc_object *object; "; + Preamble += "struct objc_object *superClass; "; + if (LangOpts.Microsoft) { + // Add a constructor for creating temporary objects. + Preamble += "__rw_objc_super(struct objc_object *o, struct objc_object *s) " + ": "; + Preamble += "object(o), superClass(s) {} "; + } + Preamble += "};\n"; + Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; + Preamble += "typedef struct objc_object Protocol;\n"; + Preamble += "#define _REWRITER_typedef_Protocol\n"; + Preamble += "#endif\n"; + if (LangOpts.Microsoft) { + Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; + Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; + } else + Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend_stret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper_stret"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT double objc_msgSend_fpret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_enter(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_exit(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_exception_extract(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_exception_match"; + Preamble += "(struct objc_class *, struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_enter(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_exit(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; + Preamble += "struct __objcFastEnumerationState {\n\t"; + Preamble += "unsigned long state;\n\t"; + Preamble += "void **itemsPtr;\n\t"; + Preamble += "unsigned long *mutationsPtr;\n\t"; + Preamble += "unsigned long extra[5];\n};\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; + Preamble += "#define __FASTENUMERATIONSTATE\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; + Preamble += "struct __NSConstantStringImpl {\n"; + Preamble += " int *isa;\n"; + Preamble += " int flags;\n"; + Preamble += " char *str;\n"; + Preamble += " long length;\n"; + Preamble += "};\n"; + Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; + Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; + Preamble += "#endif\n"; + Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; + Preamble += "#endif\n"; + // Blocks preamble. + Preamble += "#ifndef BLOCK_IMPL\n"; + Preamble += "#define BLOCK_IMPL\n"; + Preamble += "struct __block_impl {\n"; + Preamble += " void *isa;\n"; + Preamble += " int Flags;\n"; + Preamble += " int Size;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "__OBJC_RW_STATICIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_STATICIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_STATICIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_STATICIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + if (LangOpts.Microsoft) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#define __attribute__(X)\n"; + } +} + + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) { + // Two cases: either the decl could be in the main file, or it could be in a + // #included file. If the former, rewrite it now. If the later, check to see + // if we rewrote the #include/#import. + SourceLocation Loc = D->getLocation(); + Loc = SM->getInstantiationLoc(Loc); + + // If this is for a builtin, ignore it. + if (Loc.isInvalid()) return; + + // Look for built-in declarations that we need to refer during the rewrite. + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + RewriteFunctionDecl(FD); + } else if (VarDecl *FVD = dyn_cast<VarDecl>(D)) { + // declared in <Foundation/NSString.h> + if (strcmp(FVD->getNameAsCString(), "_NSConstantStringClassReference") == 0) { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCInterfaceDecl *MD = dyn_cast<ObjCInterfaceDecl>(D)) { + RewriteInterfaceDecl(MD); + } else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { + RewriteProtocolDecl(PD); + } else if (ObjCForwardProtocolDecl *FP = + dyn_cast<ObjCForwardProtocolDecl>(D)){ + RewriteForwardProtocolDecl(FP); + } else if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(*Context), + DIEnd = LSD->decls_end(*Context); + DI != DIEnd; ++DI) + HandleTopLevelSingleDecl(*DI); + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isFromMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.first; + const char *MainBufEnd = MainBuf.second; + size_t ImportLen = strlen("import"); + size_t IncludeLen = strlen("include"); + + // Loop over the whole file, looking for includes. + for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { + if (*BufPtr == '#') { + if (++BufPtr == MainBufEnd) + return; + while (*BufPtr == ' ' || *BufPtr == '\t') + if (++BufPtr == MainBufEnd) + return; + if (!strncmp(BufPtr, "import", ImportLen)) { + // replace import with include + SourceLocation ImportLoc = + LocStart.getFileLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include", IncludeLen); + BufPtr += ImportLen; + } + } + } +} + +void RewriteObjC::RewriteTabs() { + std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.first; + const char *MainBufEnd = MainBuf.second; + + // Loop over the whole file, looking for tabs. + for (const char *BufPtr = MainBufStart; BufPtr != MainBufEnd; ++BufPtr) { + if (*BufPtr != '\t') + continue; + + // Okay, we found a tab. This tab will turn into at least one character, + // but it depends on which 'virtual column' it is in. Compute that now. + unsigned VCol = 0; + while (BufPtr-VCol != MainBufStart && BufPtr[-VCol-1] != '\t' && + BufPtr[-VCol-1] != '\n' && BufPtr[-VCol-1] != '\r') + ++VCol; + + // Okay, now that we know the virtual column, we know how many spaces to + // insert. We assume 8-character tab-stops. + unsigned Spaces = 8-(VCol & 7); + + // Get the location of the tab. + SourceLocation TabLoc = SM->getLocForStartOfFile(MainFileID); + TabLoc = TabLoc.getFileLocWithOffset(BufPtr-MainBufStart); + + // Rewrite the single tab character into a sequence of spaces. + ReplaceText(TabLoc, 1, " ", Spaces); + } +} + +static std::string getIvarAccessString(ObjCInterfaceDecl *ClassDecl, + ObjCIvarDecl *OID) { + std::string S; + S = "((struct "; + S += ClassDecl->getIdentifier()->getName(); + S += "_IMPL *)self)->"; + S += OID->getNameAsCString(); + return S; +} + +void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// ", 3); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + SourceLocation onePastSemiLoc = + startLoc.getFileLocWithOffset(semiBuf-startBuf+1); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCInterfaceDecl *ClassDecl = PD->getGetterMethodDecl()->getClassInterface(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + + if (!OID) + return; + + std::string Getr; + RewriteObjCMethodDecl(PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // FIXME: deal with code generation implications for various property + // attributes (copy, retain, nonatomic). + // See objc-act.c:objc_synthesize_new_getter() for details. + Getr += "return " + getIvarAccessString(ClassDecl, OID); + Getr += "; }"; + InsertText(onePastSemiLoc, Getr.c_str(), Getr.size()); + if (PD->isReadOnly()) + return; + + // Generate the 'setter' function. + std::string Setr; + RewriteObjCMethodDecl(PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // FIXME: deal with code generation implications for various property + // attributes (copy, retain, nonatomic). + // See objc-act.c:objc_synthesize_new_setter() for details. + Setr += getIvarAccessString(ClassDecl, OID) + " = "; + Setr += PD->getNameAsCString(); + Setr += "; }"; + InsertText(onePastSemiLoc, Setr.c_str(), Setr.size()); +} + +void RewriteObjC::RewriteForwardClassDecl(ObjCClassDecl *ClassDecl) { + // Get the start location and compute the semi location. + SourceLocation startLoc = ClassDecl->getLocation(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + + // Translate to typedef's that forward reference structs with the same name + // as the class. As a convenience, we include the original declaration + // as a comment. + std::string typedefString; + typedefString += "// "; + typedefString.append(startBuf, semiPtr-startBuf+1); + typedefString += "\n"; + for (ObjCClassDecl::iterator I = ClassDecl->begin(), E = ClassDecl->end(); + I != E; ++I) { + ObjCInterfaceDecl *ForwardDecl = *I; + typedefString += "#ifndef _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "#define _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "typedef struct objc_object "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; + } + + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, + typedefString.c_str(), typedefString.size()); +} + +void RewriteObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + SourceLocation LocStart = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + + if (SM->getInstantiationLineNumber(LocEnd) > + SM->getInstantiationLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n", 6); + ReplaceText(LocEnd, 1, ";\n#endif\n", 9); + } else { + InsertText(LocStart, "// ", 3); + } +} + +void RewriteObjC::RewriteProperty(ObjCPropertyDecl *prop) +{ + SourceLocation Loc = prop->getLocation(); + + ReplaceText(Loc, 0, "// ", 3); + + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getLocStart(); + + // FIXME: handle category headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// ", 3); + + for (ObjCCategoryDecl::instmeth_iterator + I = CatDecl->instmeth_begin(*Context), + E = CatDecl->instmeth_end(*Context); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCCategoryDecl::classmeth_iterator + I = CatDecl->classmeth_begin(*Context), + E = CatDecl->classmeth_end(*Context); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndLoc(), 0, "// ", 3); +} + +void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID); + + SourceLocation LocStart = PDecl->getLocStart(); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// ", 3); + + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(*Context), + E = PDecl->instmeth_end(*Context); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(*Context), + E = PDecl->classmeth_end(*Context); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndLoc(); + ReplaceText(LocEnd, 0, "// ", 3); + + // Must comment out @optional/@required + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + for (const char *p = startBuf; p < endBuf; p++) { + if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { + std::string CommentedOptional = "/* @optional */"; + SourceLocation OptionalLoc = LocStart.getFileLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), + CommentedOptional.c_str(), CommentedOptional.size()); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + std::string CommentedRequired = "/* @required */"; + SourceLocation OptionalLoc = LocStart.getFileLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), + CommentedRequired.c_str(), CommentedRequired.size()); + + } + } +} + +void RewriteObjC::RewriteForwardProtocolDecl(ObjCForwardProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getLocation(); + if (LocStart.isInvalid()) + assert(false && "Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// ", 3); +} + +void RewriteObjC::RewriteObjCMethodDecl(ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = 0; + ResultStr += "\nstatic "; + if (OMD->getResultType()->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (OMD->getResultType()->isFunctionPointerType() || + OMD->getResultType()->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = OMD->getResultType(); + QualType PointeeTy; + if (const PointerType* PT = retType->getAsPointerType()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAsBlockPointerType()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAsFunctionType())) { + ResultStr += FPRetType->getResultType().getAsString(); + ResultStr += "(*"; + } + } else + ResultStr += OMD->getResultType().getAsString(); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += OMD->getClassInterface()->getNameAsString(); + NameStr += "_"; + + if (ObjCCategoryImplDecl *CID = + dyn_cast<ObjCCategoryImplDecl>(OMD->getDeclContext())) { + NameStr += CID->getNameAsString(); + NameStr += "_"; + } + // Append selector names, replacing ':' with '_' + { + std::string selString = OMD->getSelector().getAsString(); + int len = selString.size(); + for (int i = 0; i < len; i++) + if (selString[i] == ':') + selString[i] = '_'; + NameStr += selString; + } + // Remember this name for metadata emission + MethodInternalNames[OMD] = NameStr; + ResultStr += NameStr; |