diff options
| author | Daniel Dunbar <daniel@zuster.org> | 2009-03-24 03:00:12 +0000 |
|---|---|---|
| committer | Daniel Dunbar <daniel@zuster.org> | 2009-03-24 03:00:12 +0000 |
| commit | e5a7ecc23afc0b7c0548c1e1d1f3a73e5811891e (patch) | |
| tree | 6f57deeeb880aa041fb9dc03dc73af18b3423bad /clang/tools/clang-cc | |
| parent | 3db73ab7f6d08b17e5a59088d9df39983b048946 (diff) | |
| download | bcm5719-llvm-e5a7ecc23afc0b7c0548c1e1d1f3a73e5811891e.tar.gz bcm5719-llvm-e5a7ecc23afc0b7c0548c1e1d1f3a73e5811891e.zip | |
Move <root>/Driver into <root>/tools/clang-cc.
Again, I tried to update cmake but it is untested.
llvm-svn: 67605
Diffstat (limited to 'clang/tools/clang-cc')
21 files changed, 13485 insertions, 0 deletions
diff --git a/clang/tools/clang-cc/ASTConsumers.cpp b/clang/tools/clang-cc/ASTConsumers.cpp new file mode 100644 index 00000000000..9eba137918e --- /dev/null +++ b/clang/tools/clang-cc/ASTConsumers.cpp @@ -0,0 +1,1074 @@ +//===--- ASTConsumers.cpp - ASTConsumer implementations -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// AST Consumer Implementations. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/AST/TranslationUnit.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "llvm/Module.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +/// DeclPrinter - Utility class for printing top-level decls. + +namespace { + class DeclPrinter { + public: + llvm::raw_ostream& Out; + unsigned Indentation; + + DeclPrinter(llvm::raw_ostream* out) : Out(out ? *out : llvm::errs()), + Indentation(0) {} + DeclPrinter() : Out(llvm::errs()), Indentation(0) {} + virtual ~DeclPrinter(); + + void ChangeIndent(int I) { + Indentation += I; + } + + llvm::raw_ostream& Indent() { + for (unsigned i = 0; i < Indentation; ++i) + Out << " "; + return Out; + } + + void PrintDecl(Decl *D); + void Print(NamedDecl *ND); + void Print(NamespaceDecl *NS); + void PrintFunctionDeclStart(FunctionDecl *FD); + void PrintTypeDefDecl(TypedefDecl *TD); + void PrintLinkageSpec(LinkageSpecDecl *LS); + void PrintObjCMethodDecl(ObjCMethodDecl *OMD); + void PrintObjCImplementationDecl(ObjCImplementationDecl *OID); + void PrintObjCInterfaceDecl(ObjCInterfaceDecl *OID); + void PrintObjCProtocolDecl(ObjCProtocolDecl *PID); + void PrintObjCCategoryImplDecl(ObjCCategoryImplDecl *PID); + void PrintObjCCategoryDecl(ObjCCategoryDecl *PID); + void PrintObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *AID); + void PrintObjCPropertyDecl(ObjCPropertyDecl *PD); + void PrintObjCPropertyImplDecl(ObjCPropertyImplDecl *PID); + + void PrintTemplateDecl(TemplateDecl *TD); + }; +} // end anonymous namespace + +DeclPrinter::~DeclPrinter() { + Out.flush(); +} + +void DeclPrinter:: PrintDecl(Decl *D) { + Indent(); + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + PrintFunctionDeclStart(FD); + + if (FD->getBody()) { + Out << ' '; + FD->getBody()->printPretty(Out, 0, Indentation, true); + Out << '\n'; + } + } else if (isa<ObjCMethodDecl>(D)) { + // Do nothing, methods definitions are printed in + // PrintObjCImplementationDecl. + } else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { + PrintTypeDefDecl(TD); + } else if (ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(D)) { + PrintObjCInterfaceDecl(OID); + } else if (ObjCProtocolDecl *PID = dyn_cast<ObjCProtocolDecl>(D)) { + PrintObjCProtocolDecl(PID); + } else if (ObjCForwardProtocolDecl *OFPD = + dyn_cast<ObjCForwardProtocolDecl>(D)) { + Out << "@protocol "; + for (ObjCForwardProtocolDecl::iterator I = OFPD->begin(), E = OFPD->end(); + I != E; ++I) { + if (I != OFPD->begin()) Out << ", "; + Out << (*I)->getNameAsString(); + } + Out << ";\n"; + } else if (ObjCImplementationDecl *OID = + dyn_cast<ObjCImplementationDecl>(D)) { + PrintObjCImplementationDecl(OID); + } else if (ObjCCategoryImplDecl *OID = + dyn_cast<ObjCCategoryImplDecl>(D)) { + PrintObjCCategoryImplDecl(OID); + } else if (ObjCCategoryDecl *OID = + dyn_cast<ObjCCategoryDecl>(D)) { + PrintObjCCategoryDecl(OID); + } else if (ObjCCompatibleAliasDecl *OID = + dyn_cast<ObjCCompatibleAliasDecl>(D)) { + PrintObjCCompatibleAliasDecl(OID); + } else if (ObjCClassDecl *OFCD = dyn_cast<ObjCClassDecl>(D)) { + Out << "@class "; + for (ObjCClassDecl::iterator I = OFCD->begin(), E = OFCD->end(); + I != E; ++I) { + if (I != OFCD->begin()) Out << ", "; + Out << (*I)->getNameAsString(); + } + Out << ";\n"; + } else if (EnumDecl *ED = dyn_cast<EnumDecl>(D)) { + Out << "enum " << ED->getNameAsString() << " {\n"; + for (EnumDecl::enumerator_iterator E = ED->enumerator_begin(), + EEnd = ED->enumerator_end(); + E != EEnd; ++E) + Out << " " << (*E)->getNameAsString() << ",\n"; + Out << "};\n"; + } else if (TagDecl *TD = dyn_cast<TagDecl>(D)) { + // print a free standing tag decl (e.g. "struct x;"). + Out << TD->getKindName(); + Out << " "; + if (const IdentifierInfo *II = TD->getIdentifier()) + Out << II->getName(); + + Out << " {\n"; + ChangeIndent(1); + for (DeclContext::decl_iterator i = TD->decls_begin(); + i != TD->decls_end(); + ++i) + PrintDecl(*i); + ChangeIndent(-1); + Indent(); + Out << "}"; + + Out << "\n"; + } else if (TemplateDecl *TempD = dyn_cast<TemplateDecl>(D)) { + PrintTemplateDecl(TempD); + } else if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { + PrintLinkageSpec(LSD); + } else if (FileScopeAsmDecl *AD = dyn_cast<FileScopeAsmDecl>(D)) { + Out << "asm("; + AD->getAsmString()->printPretty(Out); + Out << ")\n"; + } else if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + Print(ND); + } else { + assert(0 && "Unknown decl type!"); + } +} + +void DeclPrinter::Print(NamedDecl *ND) { + switch (ND->getKind()) { + default: + // FIXME: Handle the rest of the NamedDecls. + Out << "### NamedDecl " << ND->getNameAsString() << "\n"; + break; + case Decl::Field: + case Decl::Var: { + // Emit storage class for vardecls. + if (VarDecl *V = dyn_cast<VarDecl>(ND)) { + switch (V->getStorageClass()) { + default: assert(0 && "Unknown storage class!"); + case VarDecl::None: break; + case VarDecl::Auto: Out << "auto "; break; + case VarDecl::Register: Out << "register "; break; + case VarDecl::Extern: Out << "extern "; break; + case VarDecl::Static: Out << "static "; break; + case VarDecl::PrivateExtern: Out << "__private_extern__ "; break; + } + } + std::string Name = ND->getNameAsString(); + // This forms: "int a". + dyn_cast<ValueDecl>(ND)->getType().getAsStringInternal(Name); + Out << Name << ";\n"; + break; + } + case Decl::Namespace: + Print(dyn_cast<NamespaceDecl>(ND)); + break; + } +} + +void DeclPrinter::Print(NamespaceDecl *NS) { + Out << "namespace " << NS->getNameAsString() << " {\n"; + ChangeIndent(1); + for (DeclContext::decl_iterator i = NS->decls_begin(); + i != NS->decls_end(); + ++i) + PrintDecl(*i); + ChangeIndent(-1); + Indent(); + Out << "}\n"; +} + +void DeclPrinter::PrintFunctionDeclStart(FunctionDecl *FD) { + bool HasBody = FD->getBody(); + + Out << '\n'; + + Indent(); + switch (FD->getStorageClass()) { + default: assert(0 && "Unknown storage class"); + case FunctionDecl::None: break; + case FunctionDecl::Extern: Out << "extern "; break; + case FunctionDecl::Static: Out << "static "; break; + case FunctionDecl::PrivateExtern: Out << "__private_extern__ "; break; + } + + if (FD->isInline()) + Out << "inline "; + + std::string Proto = FD->getNameAsString(); + const FunctionType *AFT = FD->getType()->getAsFunctionType(); + + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(AFT)) { + Proto += "("; + for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) { + if (i) Proto += ", "; + std::string ParamStr; + if (HasBody) ParamStr = FD->getParamDecl(i)->getNameAsString(); + + FT->getArgType(i).getAsStringInternal(ParamStr); + Proto += ParamStr; + } + + if (FT->isVariadic()) { + if (FD->getNumParams()) Proto += ", "; + Proto += "..."; + } + Proto += ")"; + } else { + assert(isa<FunctionNoProtoType>(AFT)); + Proto += "()"; + } + + AFT->getResultType().getAsStringInternal(Proto); + Out << Proto; + + if (!FD->getBody()) + Out << ";\n"; + // Doesn't print the body. +} + +void DeclPrinter::PrintTypeDefDecl(TypedefDecl *TD) { + std::string S = TD->getNameAsString(); + TD->getUnderlyingType().getAsStringInternal(S); + Out << "typedef " << S << ";\n"; +} + +void DeclPrinter::PrintLinkageSpec(LinkageSpecDecl *LS) { + const char *l; + if (LS->getLanguage() == LinkageSpecDecl::lang_c) + l = "C"; + else { + assert(LS->getLanguage() == LinkageSpecDecl::lang_cxx && + "unknown language in linkage specification"); + l = "C++"; + } + + Out << "extern \"" << l << "\" "; + if (LS->hasBraces()) { + Out << "{\n"; + ChangeIndent(1); + } + + for (LinkageSpecDecl::decl_iterator D = LS->decls_begin(), + DEnd = LS->decls_end(); + D != DEnd; ++D) + PrintDecl(*D); + + if (LS->hasBraces()) { + ChangeIndent(-1); + Indent() << "}"; + } + Out << "\n"; +} + +void DeclPrinter::PrintObjCMethodDecl(ObjCMethodDecl *OMD) { + if (OMD->isInstanceMethod()) + Out << "\n- "; + else + Out << "\n+ "; + if (!OMD->getResultType().isNull()) + Out << '(' << OMD->getResultType().getAsString() << ")"; + + std::string name = OMD->getSelector().getAsString(); + std::string::size_type pos, lastPos = 0; + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + // FIXME: selector is missing here! + pos = name.find_first_of(":", lastPos); + Out << " " << name.substr(lastPos, pos - lastPos); + Out << ":(" << (*PI)->getType().getAsString() << ")" + << (*PI)->getNameAsString(); + lastPos = pos + 1; + } + + if (OMD->param_begin() == OMD->param_end()) + Out << " " << name; + + if (OMD->isVariadic()) + Out << ", ..."; + + Out << ";"; +} + +void DeclPrinter::PrintObjCImplementationDecl(ObjCImplementationDecl *OID) { + std::string I = OID->getNameAsString(); + ObjCInterfaceDecl *SID = OID->getSuperClass(); + + if (SID) + Out << "@implementation " << I << " : " << SID->getNameAsString(); + else + Out << "@implementation " << I; + + for (ObjCImplementationDecl::instmeth_iterator I = OID->instmeth_begin(), + E = OID->instmeth_end(); I != E; ++I) { + ObjCMethodDecl *OMD = *I; + PrintObjCMethodDecl(OMD); + if (OMD->getBody()) { + Out << ' '; + OMD->getBody()->printPretty(Out); + Out << '\n'; + } + } + + for (ObjCImplementationDecl::classmeth_iterator I = OID->classmeth_begin(), + E = OID->classmeth_end(); I != E; ++I) { + ObjCMethodDecl *OMD = *I; + PrintObjCMethodDecl(OMD); + if (OMD->getBody()) { + Out << ' '; + OMD->getBody()->printPretty(Out); + Out << '\n'; + } + } + + for (ObjCImplementationDecl::propimpl_iterator I = OID->propimpl_begin(), + E = OID->propimpl_end(); I != E; ++I) + PrintObjCPropertyImplDecl(*I); + + Out << "@end\n"; +} + + +void DeclPrinter::PrintObjCInterfaceDecl(ObjCInterfaceDecl *OID) { + std::string I = OID->getNameAsString(); + ObjCInterfaceDecl *SID = OID->getSuperClass(); + + if (SID) + Out << "@interface " << I << " : " << SID->getNameAsString(); + else + Out << "@interface " << I; + + // Protocols? + const ObjCList<ObjCProtocolDecl> &Protocols = OID->getReferencedProtocols(); + if (!Protocols.empty()) { + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), + E = Protocols.end(); I != E; ++I) + Out << (I == Protocols.begin() ? '<' : ',') << (*I)->getNameAsString(); + } + + if (!Protocols.empty()) + Out << ">"; + Out << '\n'; + + if (OID->ivar_size() > 0) { + Out << '{'; + for (ObjCInterfaceDecl::ivar_iterator I = OID->ivar_begin(), + E = OID->ivar_end(); I != E; ++I) { + Out << '\t' << (*I)->getType().getAsString() + << ' ' << (*I)->getNameAsString() << ";\n"; + } + Out << "}\n"; + } + + for (ObjCInterfaceDecl::prop_iterator I = OID->prop_begin(), + E = OID->prop_end(); I != E; ++I) + PrintObjCPropertyDecl(*I); + bool eol_needed = false; + for (ObjCInterfaceDecl::classmeth_iterator I = OID->classmeth_begin(), + E = OID->classmeth_end(); I != E; ++I) + eol_needed = true, PrintObjCMethodDecl(*I); + + for (ObjCInterfaceDecl::instmeth_iterator I = OID->instmeth_begin(), + E = OID->instmeth_end(); I != E; ++I) + eol_needed = true, PrintObjCMethodDecl(*I); + + Out << (eol_needed ? "\n@end\n" : "@end\n"); + // FIXME: implement the rest... +} + +void DeclPrinter::PrintObjCProtocolDecl(ObjCProtocolDecl *PID) { + Out << "@protocol " << PID->getNameAsString() << '\n'; + + for (ObjCProtocolDecl::prop_iterator I = PID->prop_begin(), + E = PID->prop_end(); I != E; ++I) + PrintObjCPropertyDecl(*I); + Out << "@end\n"; + // FIXME: implement the rest... +} + +void DeclPrinter::PrintObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) { + Out << "@implementation " + << PID->getClassInterface()->getNameAsString() + << '(' << PID->getNameAsString() << ");\n"; + for (ObjCCategoryImplDecl::propimpl_iterator I = PID->propimpl_begin(), + E = PID->propimpl_end(); I != E; ++I) + PrintObjCPropertyImplDecl(*I); + Out << "@end\n"; + // FIXME: implement the rest... +} + +void DeclPrinter::PrintObjCCategoryDecl(ObjCCategoryDecl *PID) { + Out << "@interface " + << PID->getClassInterface()->getNameAsString() + << '(' << PID->getNameAsString() << ");\n"; + // Output property declarations. + for (ObjCCategoryDecl::prop_iterator I = PID->prop_begin(), + E = PID->prop_end(); I != E; ++I) + PrintObjCPropertyDecl(*I); + Out << "@end\n"; + + // FIXME: implement the rest... +} + +void DeclPrinter::PrintObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *AID) { + Out << "@compatibility_alias " << AID->getNameAsString() + << ' ' << AID->getClassInterface()->getNameAsString() << ";\n"; +} + +/// PrintObjCPropertyDecl - print a property declaration. +/// +void DeclPrinter::PrintObjCPropertyDecl(ObjCPropertyDecl *PDecl) { + if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Required) + Out << "@required\n"; + else if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Optional) + Out << "@optional\n"; + + Out << "@property"; + if (PDecl->getPropertyAttributes() != ObjCPropertyDecl::OBJC_PR_noattr) { + bool first = true; + Out << " ("; + if (PDecl->getPropertyAttributes() & + ObjCPropertyDecl::OBJC_PR_readonly) { + Out << (first ? ' ' : ',') << "readonly"; + first = false; + } + + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_getter) { + Out << (first ? ' ' : ',') << "getter = " + << PDecl->getGetterName().getAsString(); + first = false; + } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_setter) { + Out << (first ? ' ' : ',') << "setter = " + << PDecl->getSetterName().getAsString(); + first = false; + } + + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_assign) { + Out << (first ? ' ' : ',') << "assign"; + first = false; + } + + if (PDecl->getPropertyAttributes() & + ObjCPropertyDecl::OBJC_PR_readwrite) { + Out << (first ? ' ' : ',') << "readwrite"; + first = false; + } + + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_retain) { + Out << (first ? ' ' : ',') << "retain"; + first = false; + } + + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_copy) { + Out << (first ? ' ' : ',') << "copy"; + first = false; + } + + if (PDecl->getPropertyAttributes() & + ObjCPropertyDecl::OBJC_PR_nonatomic) { + Out << (first ? ' ' : ',') << "nonatomic"; + first = false; + } + Out << " )"; + } + Out << ' ' << PDecl->getType().getAsString() + << ' ' << PDecl->getNameAsString(); + + Out << ";\n"; +} + +/// PrintObjCPropertyImplDecl - Print an objective-c property implementation +/// declaration syntax. +/// +void DeclPrinter::PrintObjCPropertyImplDecl(ObjCPropertyImplDecl *PID) { + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) + Out << "\n@synthesize "; + else + Out << "\n@dynamic "; + Out << PID->getPropertyDecl()->getNameAsString(); + if (PID->getPropertyIvarDecl()) + Out << "=" << PID->getPropertyIvarDecl()->getNameAsString(); + Out << ";\n"; +} + +/// PrintTemplateParams - Print a template parameter list and recursively print +/// it's underlying top-level definition. +void DeclPrinter::PrintTemplateDecl(TemplateDecl *TD) { + // TODO: Write template parameters. + Out << "template <...> "; + PrintDecl(TD->getTemplatedDecl()); +} + + + +//===----------------------------------------------------------------------===// +/// ASTPrinter - Pretty-printer of ASTs + +namespace { + class ASTPrinter : public ASTConsumer, public DeclPrinter { + public: + ASTPrinter(llvm::raw_ostream* o = NULL) : DeclPrinter(o) {} + + virtual void HandleTopLevelDecl(Decl *D) { + PrintDecl(D); + } + }; +} + +ASTConsumer *clang::CreateASTPrinter(llvm::raw_ostream* out) { + return new ASTPrinter(out); +} + +//===----------------------------------------------------------------------===// +/// ASTDumper - Low-level dumper of ASTs + +namespace { + class ASTDumper : public ASTConsumer, public DeclPrinter { + SourceManager *SM; + public: + ASTDumper() : DeclPrinter() {} + + void Initialize(ASTContext &Context) { + SM = &Context.getSourceManager(); + } + + virtual void HandleTopLevelDecl(Decl *D) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + PrintFunctionDeclStart(FD); + + if (FD->getBody()) { + Out << '\n'; + // FIXME: convert dumper to use std::ostream? + FD->getBody()->dumpAll(*SM); + Out << '\n'; + } + } else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { + PrintTypeDefDecl(TD); + } else if (ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(D)) { + Out << "Read objc interface '" << OID->getNameAsString() << "'\n"; + } else if (ObjCProtocolDecl *OPD = dyn_cast<ObjCProtocolDecl>(D)) { + Out << "Read objc protocol '" << OPD->getNameAsString() << "'\n"; + } else if (ObjCCategoryDecl *OCD = dyn_cast<ObjCCategoryDecl>(D)) { + Out << "Read objc category '" << OCD->getNameAsString() << "'\n"; + } else if (isa<ObjCForwardProtocolDecl>(D)) { + Out << "Read objc fwd protocol decl\n"; + } else if (isa<ObjCClassDecl>(D)) { + Out << "Read objc fwd class decl\n"; + } else if (isa<FileScopeAsmDecl>(D)) { + Out << "Read file scope asm decl\n"; + } else if (ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(D)) { + Out << "Read objc method decl: '" << MD->getSelector().getAsString() + << "'\n"; + if (MD->getBody()) { + // FIXME: convert dumper to use std::ostream? + MD->getBody()->dumpAll(*SM); + Out << '\n'; + } + } else if (isa<ObjCImplementationDecl>(D)) { + Out << "Read objc implementation decl\n"; + } else if (isa<ObjCCategoryImplDecl>(D)) { + Out << "Read objc category implementation decl\n"; + } else if (isa<LinkageSpecDecl>(D)) { + Out << "Read linkage spec decl\n"; + } else if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + Out << "Read top-level variable decl: '" << ND->getNameAsString() + << "'\n"; + } else { + assert(0 && "Unknown decl type!"); + } + } + }; +} + +ASTConsumer *clang::CreateASTDumper() { return new ASTDumper(); } + +//===----------------------------------------------------------------------===// +/// ASTViewer - AST Visualization + +namespace { + class ASTViewer : public ASTConsumer { + SourceManager *SM; + public: + void Initialize(ASTContext &Context) { + SM = &Context.getSourceManager(); + } + + virtual void HandleTopLevelDecl(Decl *D) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + DeclPrinter().PrintFunctionDeclStart(FD); + + if (FD->getBody()) { + llvm::cerr << '\n'; + FD->getBody()->viewAST(); + llvm::cerr << '\n'; + } + } + else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + DeclPrinter().PrintObjCMethodDecl(MD); + + if (MD->getBody()) { + llvm::cerr << '\n'; + MD->getBody()->viewAST(); + llvm::cerr << '\n'; + } + } + } + }; +} + +ASTConsumer *clang::CreateASTViewer() { return new ASTViewer(); } + +//===----------------------------------------------------------------------===// +/// DeclContextPrinter - Decl and DeclContext Visualization + +namespace { + +class DeclContextPrinter : public ASTConsumer { + llvm::raw_ostream& Out; +public: + DeclContextPrinter() : Out(llvm::errs()) {} + + void HandleTranslationUnit(TranslationUnit& TU) { + TranslationUnitDecl* TUD = TU.getContext().getTranslationUnitDecl(); + PrintDeclContext(TUD, 4); + } + + void PrintDeclContext(const DeclContext* DC, unsigned Indentation); +}; + +void DeclContextPrinter::PrintDeclContext(const DeclContext* DC, + unsigned Indentation) { + // Print DeclContext name. + switch (DC->getDeclKind()) { + case Decl::TranslationUnit: + Out << "[translation unit] " << DC; + break; + case Decl::Namespace: { + Out << "[namespace] "; + const NamespaceDecl* ND = cast<NamespaceDecl>(DC); + Out << ND->getNameAsString(); + break; + } + case Decl::Enum: { + const EnumDecl* ED = cast<EnumDecl>(DC); + if (ED->isDefinition()) + Out << "[enum] "; + else + Out << "<enum> "; + Out << ED->getNameAsString(); + break; + } + case Decl::Record: { + const RecordDecl* RD = cast<RecordDecl>(DC); + if (RD->isDefinition()) + Out << "[struct] "; + else + Out << "<struct> "; + Out << RD->getNameAsString(); + break; + } + case Decl::CXXRecord: { + const CXXRecordDecl* RD = cast<CXXRecordDecl>(DC); + if (RD->isDefinition()) + Out << "[class] "; + else + Out << "<class> "; + Out << RD->getNameAsString() << " " << DC; + break; + } + case Decl::ObjCMethod: + Out << "[objc method]"; + break; + case Decl::ObjCInterface: + Out << "[objc interface]"; + break; + case Decl::ObjCCategory: + Out << "[objc category]"; + break; + case Decl::ObjCProtocol: + Out << "[objc protocol]"; + break; + case Decl::ObjCImplementation: + Out << "[objc implementation]"; + break; + case Decl::ObjCCategoryImpl: + Out << "[objc categoryimpl]"; + break; + case Decl::LinkageSpec: + Out << "[linkage spec]"; + break; + case Decl::Block: + Out << "[block]"; + break; + case Decl::Function: { + const FunctionDecl* FD = cast<FunctionDecl>(DC); + if (FD->isThisDeclarationADefinition()) + Out << "[function] "; + else + Out << "<function> "; + Out << FD->getNameAsString(); + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = FD->param_begin(), + E = FD->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << (*I)->getNameAsString(); + } + Out << ")"; + break; + } + case Decl::CXXMethod: { + const CXXMethodDecl* D = cast<CXXMethodDecl>(DC); + if (D->isOutOfLineDefinition()) + Out << "[c++ method] "; + else if (D->isImplicit()) + Out << "(c++ method) "; + else + Out << "<c++ method> "; + Out << D->getNameAsString(); + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << (*I)->getNameAsString(); + } + Out << ")"; + + // Check the semantic DeclContext. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + + break; + } + case Decl::CXXConstructor: { + const CXXConstructorDecl* D = cast<CXXConstructorDecl>(DC); + if (D->isOutOfLineDefinition()) + Out << "[c++ ctor] "; + else if (D->isImplicit()) + Out << "(c++ ctor) "; + else + Out << "<c++ ctor> "; + Out << D->getNameAsString(); + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << (*I)->getNameAsString(); + } + Out << ")"; + + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + case Decl::CXXDestructor: { + const CXXDestructorDecl* D = cast<CXXDestructorDecl>(DC); + if (D->isOutOfLineDefinition()) + Out << "[c++ dtor] "; + else if (D->isImplicit()) + Out << "(c++ dtor) "; + else + Out << "<c++ dtor> "; + Out << D->getNameAsString(); + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + case Decl::CXXConversion: { + const CXXConversionDecl* D = cast<CXXConversionDecl>(DC); + if (D->isOutOfLineDefinition()) + Out << "[c++ conversion] "; + else if (D->isImplicit()) + Out << "(c++ conversion) "; + else + Out << "<c++ conversion> "; + Out << D->getNameAsString(); + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + + default: + assert(0 && "a decl that inherits DeclContext isn't handled"); + } + + Out << "\n"; + + // Print decls in the DeclContext. + for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); + I != E; ++I) { + for (unsigned i = 0; i < Indentation; ++i) + Out << " "; + + Decl::Kind DK = I->getKind(); + switch (DK) { + case Decl::Namespace: + case Decl::Enum: + case Decl::Record: + case Decl::CXXRecord: + case Decl::ObjCMethod: + case Decl::ObjCInterface: + case Decl::ObjCCategory: + case Decl::ObjCProtocol: + case Decl::ObjCImplementation: + case Decl::ObjCCategoryImpl: + case Decl::LinkageSpec: + case Decl::Block: + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + { + DeclContext* DC = cast<DeclContext>(*I); + PrintDeclContext(DC, Indentation+2); + break; + } + case Decl::Field: { + FieldDecl* FD = cast<FieldDecl>(*I); + Out << "<field> " << FD->getNameAsString() << "\n"; + break; + } + case Decl::Typedef: { + TypedefDecl* TD = cast<TypedefDecl>(*I); + Out << "<typedef> " << TD->getNameAsString() << "\n"; + break; + } + case Decl::EnumConstant: { + EnumConstantDecl* ECD = cast<EnumConstantDecl>(*I); + Out << "<enum constant> " << ECD->getNameAsString() << "\n"; + break; + } + case Decl::Var: { + VarDecl* VD = cast<VarDecl>(*I); + Out << "<var> " << VD->getNameAsString() << "\n"; + break; + } + case Decl::ImplicitParam: { + ImplicitParamDecl* IPD = cast<ImplicitParamDecl>(*I); + Out << "<implicit parameter> " << IPD->getNameAsString() << "\n"; + break; + } + case Decl::ParmVar: { + ParmVarDecl* PVD = cast<ParmVarDecl>(*I); + Out << "<parameter> " << PVD->getNameAsString() << "\n"; + break; + } + case Decl::ObjCProperty: { + ObjCPropertyDecl* OPD = cast<ObjCPropertyDecl>(*I); + Out << "<objc property> " << OPD->getNameAsString() << "\n"; + break; + } + default: + fprintf(stderr, "DeclKind: %d\n", DK); + assert(0 && "decl unhandled"); + } + } +} + +} + +ASTConsumer *clang::CreateDeclContextPrinter() { + return new DeclContextPrinter(); +} + +//===----------------------------------------------------------------------===// +/// InheritanceViewer - C++ Inheritance Visualization + +namespace { +class InheritanceViewer : public ASTConsumer { + const std::string clsname; +public: + InheritanceViewer(const std::string& cname) : clsname(cname) {} + + void HandleTranslationUnit(TranslationUnit& TU) { + ASTContext& C = TU.getContext(); + for (ASTContext::type_iterator I=C.types_begin(),E=C.types_end(); I!=E; ++I) + if (RecordType *T = dyn_cast<RecordType>(*I)) { + if (CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(T->getDecl())) { + // FIXME: This lookup needs to be generalized to handle namespaces and + // (when we support them) templates. + if (D->getNameAsString() == clsname) { + D->viewInheritance(C); + } + } + } + } +}; +} + +ASTConsumer *clang::CreateInheritanceViewer(const std::string& clsname) { + return new InheritanceViewer(clsname); +} + +//===----------------------------------------------------------------------===// +// AST Serializer + +namespace { + +class ASTSerializer : public ASTConsumer { +protected: + Diagnostic& Diags; + +public: + ASTSerializer(Diagnostic& diags) : Diags(diags) {} +}; + +class SingleFileSerializer : public ASTSerializer { + const llvm::sys::Path FName; +public: + SingleFileSerializer(const llvm::sys::Path& F, Diagnostic& diags) + : ASTSerializer(diags), FName(F) {} + + virtual void HandleTranslationUnit(TranslationUnit& TU) { + if (Diags.hasErrorOccurred()) + return; + EmitASTBitcodeFile(&TU, FName); + } +}; + +class BuildSerializer : public ASTSerializer { + llvm::sys::Path EmitDir; +public: + BuildSerializer(const llvm::sys::Path& dir, Diagnostic& diags) + : ASTSerializer(diags), EmitDir(dir) {} + + virtual void HandleTranslationUnit(TranslationUnit& TU) { + if (Diags.hasErrorOccurred()) + return; + + SourceManager& SourceMgr = TU.getContext().getSourceManager(); + FileID ID = SourceMgr.getMainFileID(); + assert(!ID.isInvalid() && "MainFileID not set!"); + const FileEntry* FE = SourceMgr.getFileEntryForID(ID); + assert(FE && "No FileEntry for main file."); + + // FIXME: This is not portable to Windows. + // FIXME: This logic should probably be moved elsewhere later. + + llvm::sys::Path FName(EmitDir); + + std::vector<char> buf; + buf.reserve(strlen(FE->getName())+100); + + sprintf(&buf[0], "dev_%llx", (unsigned long long) FE->getDevice()); + FName.appendComponent(&buf[0]); + FName.createDirectoryOnDisk(true); + if (!FName.canWrite() || !FName.isDirectory()) { + assert (false && "Could not create 'device' serialization directory."); + return; + } + + sprintf(&buf[0], "%s-%llX.ast", FE->getName(), + (unsigned long long) FE->getInode()); + FName.appendComponent(&buf[0]); + EmitASTBitcodeFile(&TU, FName); + + // Now emit the sources. + + } +}; + + +} // end anonymous namespace + + +ASTConsumer* clang::CreateASTSerializer(const std::string& InFile, + const std::string& OutputFile, + Diagnostic &Diags) { + + if (OutputFile.size()) { + if (InFile == "-") { + llvm::cerr << + "error: Cannot use --serialize with -o for source read from STDIN.\n"; + return NULL; + } + + // The user specified an AST-emission directory. Determine if the path + // is absolute. + llvm::sys::Path EmitDir(OutputFile); + + if (!EmitDir.isAbsolute()) { + llvm::cerr << + "error: Output directory for --serialize must be an absolute path.\n"; + + return NULL; + } + + // Create the directory if it does not exist. + EmitDir.createDirectoryOnDisk(true); + if (!EmitDir.canWrite() || !EmitDir.isDirectory()) { + llvm::cerr << + "error: Could not create output directory for --serialize.\n"; + + return NULL; + } + + // FIXME: We should probably only allow using BuildSerializer when + // the ASTs come from parsed source files, and not from .ast files. + return new BuildSerializer(EmitDir, Diags); + } + + // The user did not specify an output directory for serialized ASTs. + // Serialize the translation to a single file whose name is the same + // as the input file with the ".ast" extension appended. + + llvm::sys::Path FName(InFile.c_str()); + FName.appendSuffix("ast"); + return new SingleFileSerializer(FName, Diags); +} diff --git a/clang/tools/clang-cc/ASTConsumers.h b/clang/tools/clang-cc/ASTConsumers.h new file mode 100644 index 00000000000..fd8416da0ea --- /dev/null +++ b/clang/tools/clang-cc/ASTConsumers.h @@ -0,0 +1,85 @@ +//===--- ASTConsumers.h - ASTConsumer implementations -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// AST Consumers. +// +//===----------------------------------------------------------------------===// + +#ifndef DRIVER_ASTCONSUMERS_H +#define DRIVER_ASTCONSUMERS_H + +#include "llvm/Support/raw_ostream.h" +#include <string> +#include <iosfwd> + +namespace llvm { + class Module; + namespace sys { class Path; } +} +namespace clang { + +class ASTConsumer; +class Diagnostic; +class FileManager; +class Preprocessor; +class PreprocessorFactory; +struct CompileOptions; +class LangOptions; + +ASTConsumer *CreateASTPrinter(llvm::raw_ostream* OS = NULL); + +ASTConsumer *CreateASTDumper(); + +ASTConsumer *CreateASTViewer(); + +ASTConsumer *CreateDeclContextPrinter(); + +ASTConsumer *CreateCodeRewriterTest(const std::string& InFile, + const std::string& OutFile, + Diagnostic &Diags, + const LangOptions &LOpts); + +enum BackendAction { + Backend_EmitAssembly, + Backend_EmitBC, + Backend_EmitLL, + Backend_EmitNothing +}; +ASTConsumer *CreateBackendConsumer(BackendAction Action, + Diagnostic &Diags, + const LangOptions &Features, + const CompileOptions &CompileOpts, + const std::string& InFile, + const std::string& OutFile); + +ASTConsumer* CreateHTMLPrinter(const std::string &OutFile, Diagnostic &D, + Preprocessor *PP, PreprocessorFactory* PPF); + +ASTConsumer *CreateSerializationTest(Diagnostic &Diags, + FileManager& FMgr); + +ASTConsumer *CreateASTSerializer(const std::string& InFile, + const std::string& EmitDir, + Diagnostic &Diags); + +ASTConsumer *CreateBlockRewriter(const std::string& InFile, + const std::string& OutFile, + Diagnostic &Diags, + const LangOptions &LangOpts); + +ASTConsumer *CreateInheritanceViewer(const std::string& clsname); + +ASTConsumer* CreateAnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& output); + +} // end clang namespace + +#endif diff --git a/clang/tools/clang-cc/Analyses.def b/clang/tools/clang-cc/Analyses.def new file mode 100644 index 00000000000..3492d09c10a --- /dev/null +++ b/clang/tools/clang-cc/Analyses.def @@ -0,0 +1,77 @@ +//===-- Analyses.def - Metadata about Static Analyses -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the set of static analyses used by AnalysisConsumer. +// +//===----------------------------------------------------------------------===// + +#ifndef ANALYSIS +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) +#endif + +ANALYSIS(CFGDump, "cfg-dump", + "Display Control-Flow Graphs", Code) + +ANALYSIS(CFGView, "cfg-view", + "View Control-Flow Graphs using GraphViz", Code) + +ANALYSIS(DisplayLiveVariables, "dump-live-variables", + "Print results of live variable analysis", Code) + +ANALYSIS(WarnDeadStores, "warn-dead-stores", + "Warn about stores to dead variables", Code) + +ANALYSIS(WarnUninitVals, "warn-uninit-values", + "Warn about uses of uninitialized variables", Code) + +ANALYSIS(WarnObjCMethSigs, "warn-objc-methodsigs", + "Warn about Objective-C method signatures with type incompatibilities", + ObjCImplementation) + +ANALYSIS(WarnObjCDealloc, "warn-objc-missing-dealloc", + "Warn about Objective-C classes that lack a correct implementation of -dealloc", + ObjCImplementation) + +ANALYSIS(WarnObjCUnusedIvars, "warn-objc-unused-ivars", + "Warn about private ivars that are never used", ObjCImplementation) + +ANALYSIS(CheckerSimple, "checker-simple", + "Perform simple path-sensitive checks.", Code) + +ANALYSIS(CheckerCFRef, "checker-cfref", + "Run the [Core] Foundation reference count checker", Code) + + +#ifndef ANALYSIS_STORE +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_STORE(BasicStore, "basic", "Use basic analyzer store", CreateBasicStoreManager) +ANALYSIS_STORE(RegionStore, "region", "Use region-based analyzer store", CreateRegionStoreManager) + +#ifndef ANALYSIS_CONSTRAINTS +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) +#endif + +ANALYSIS_CONSTRAINTS(BasicConstraints, "basic", "Use basic constraint tracking", CreateBasicConstraintManager) +ANALYSIS_CONSTRAINTS(RangeConstraints, "range", "Use constraint tracking of concrete value ranges", CreateRangeConstraintManager) + +#ifndef ANALYSIS_DIAGNOSTICS +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) +#endif + +ANALYSIS_DIAGNOSTICS(HTML, "html", "Output analysis results using HTML", CreateHTMLDiagnosticClient, false) +ANALYSIS_DIAGNOSTICS(PLIST, "plist", "Output analysis results using Plists", CreatePlistDiagnosticClient, true) + +#undef ANALYSIS +#undef ANALYSIS_STORE +#undef ANALYSIS_CONSTRAINTS +#undef ANALYSIS_DIAGNOSTICS +#undef ANALYSIS_STORE + diff --git a/clang/tools/clang-cc/AnalysisConsumer.cpp b/clang/tools/clang-cc/AnalysisConsumer.cpp new file mode 100644 index 00000000000..f19ff1787eb --- /dev/null +++ b/clang/tools/clang-cc/AnalysisConsumer.cpp @@ -0,0 +1,770 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/Frontend/ManagerRegistry.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "llvm/Support/Compiler.h" +#include "llvm/ADT/OwningPtr.h" +#include "clang/AST/CFG.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/TranslationUnit.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/LocalCheckers.h" +#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" +#include <vector> + +using namespace clang; + +static ExplodedNodeImpl::Auditor* CreateUbiViz(); + +//===----------------------------------------------------------------------===// +// Analyzer Options: available analyses. +//===----------------------------------------------------------------------===// + +/// Analysis - Set of available source code analyses. +enum Analyses { +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) NAME, +#include "Analyses.def" +NumAnalyses +}; + +static llvm::cl::list<Analyses> +AnalysisList(llvm::cl::desc("Source Code Analysis - Checks and Analyses"), +llvm::cl::values( +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE)\ +clEnumValN(NAME, CMDFLAG, DESC), +#include "Analyses.def" +clEnumValEnd)); + +//===----------------------------------------------------------------------===// +// Analyzer Options: store model. +//===----------------------------------------------------------------------===// + +/// AnalysisStores - Set of available analysis store models. +enum AnalysisStores { +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "Analyses.def" +NumStores +}; + +static llvm::cl::opt<AnalysisStores> +AnalysisStoreOpt("analyzer-store", + llvm::cl::desc("Source Code Analysis - Abstract Memory Store Models"), + llvm::cl::init(BasicStoreModel), + llvm::cl::values( +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN)\ +clEnumValN(NAME##Model, CMDFLAG, DESC), +#include "Analyses.def" +clEnumValEnd)); + +//===----------------------------------------------------------------------===// +// Analyzer Options: constraint engines. +//===----------------------------------------------------------------------===// + +/// AnalysisConstraints - Set of available constraint models. +enum AnalysisConstraints { +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) NAME##Model, +#include "Analyses.def" +NumConstraints +}; + +static llvm::cl::opt<AnalysisConstraints> +AnalysisConstraintsOpt("analyzer-constraints", + llvm::cl::desc("Source Code Analysis - Symbolic Constraint Engines"), + llvm::cl::init(RangeConstraintsModel), + llvm::cl::values( +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN)\ +clEnumValN(NAME##Model, CMDFLAG, DESC), +#include "Analyses.def" +clEnumValEnd)); + +//===----------------------------------------------------------------------===// +// Analyzer Options: diagnostic clients. +//===----------------------------------------------------------------------===// + +/// AnalysisDiagClients - Set of available diagnostic clients for rendering +/// analysis results. +enum AnalysisDiagClients { +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREAT) PD_##NAME, +#include "Analyses.def" +NUM_ANALYSIS_DIAG_CLIENTS +}; + +static llvm::cl::opt<AnalysisDiagClients> +AnalysisDiagOpt("analyzer-output", + llvm::cl::desc("Source Code Analysis - Output Options"), + llvm::cl::init(PD_HTML), + llvm::cl::values( +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREATE)\ +clEnumValN(PD_##NAME, CMDFLAG, DESC), +#include "Analyses.def" +clEnumValEnd)); + +//===----------------------------------------------------------------------===// +// Misc. fun options. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +VisualizeEGDot("analyzer-viz-egraph-graphviz", + llvm::cl::desc("Display exploded graph using GraphViz")); + +static llvm::cl::opt<bool> +VisualizeEGUbi("analyzer-viz-egraph-ubigraph", + llvm::cl::desc("Display exploded graph using Ubigraph")); + +static llvm::cl::opt<bool> +AnalyzeAll("analyzer-opt-analyze-headers", + llvm::cl::desc("Force the static analyzer to analyze " + "functions defined in header files")); + +static llvm::cl::opt<bool> +AnalyzerDisplayProgress("analyzer-display-progress", + llvm::cl::desc("Emit verbose output about the analyzer's progress.")); + +static llvm::cl::opt<bool> +PurgeDead("analyzer-purge-dead", + llvm::cl::init(true), + llvm::cl::desc("Remove dead symbols, bindings, and constraints before" + " processing a statement.")); + +static llvm::cl::opt<bool> +EagerlyAssume("analyzer-eagerly-assume", + llvm::cl::init(false), + llvm::cl::desc("Eagerly assume the truth/falseness of some " + "symbolic constraints.")); + +static llvm::cl::opt<std::string> +AnalyzeSpecificFunction("analyze-function", + llvm::cl::desc("Run analysis on specific function")); + +static llvm::cl::opt<bool> +TrimGraph("trim-egraph", + llvm::cl::desc("Only show error-related paths in the analysis graph")); + +//===----------------------------------------------------------------------===// +// Basic type definitions. +//===----------------------------------------------------------------------===// + +namespace { + class AnalysisManager; + typedef void (*CodeAction)(AnalysisManager& Mgr); +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + + class VISIBILITY_HIDDEN AnalysisConsumer : public ASTConsumer { + typedef std::vector<CodeAction> Actions; + Actions FunctionActions; + Actions ObjCMethodActions; + Actions ObjCImplementationActions; + Actions TranslationUnitActions; + + public: + const LangOptions& LOpts; + Diagnostic &Diags; + ASTContext* Ctx; + Preprocessor* PP; + PreprocessorFactory* PPF; + const std::string OutDir; + llvm::OwningPtr<PathDiagnosticClient> PD; + + AnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& outdir) + : LOpts(lopts), Diags(diags), + Ctx(0), PP(pp), PPF(ppf), + OutDir(outdir) {} + + void addCodeAction(CodeAction action) { + FunctionActions.push_back(action); + ObjCMethodActions.push_back(action); + } + + void addObjCImplementationAction(CodeAction action) { + ObjCImplementationActions.push_back(action); + } + + void addTranslationUnitAction(CodeAction action) { + TranslationUnitActions.push_back(action); + } + + virtual void Initialize(ASTContext &Context) { + Ctx = &Context; + } + + virtual void HandleTopLevelDecl(Decl *D); + virtual void HandleTranslationUnit(TranslationUnit &TU); + + void HandleCode(Decl* D, Stmt* Body, Actions& actions); + }; + + + class VISIBILITY_HIDDEN AnalysisManager : public BugReporterData { + Decl* D; Stmt* Body; + TranslationUnit* TU; + + enum AnalysisScope { ScopeTU, ScopeDecl } AScope; + + AnalysisConsumer& C; + bool DisplayedFunction; + + llvm::OwningPtr<CFG> cfg; + llvm::OwningPtr<LiveVariables> liveness; + llvm::OwningPtr<ParentMap> PM; + + // Configurable components creators. + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + public: + AnalysisManager(AnalysisConsumer& c, Decl* d, Stmt* b, bool displayProgress) + : D(d), Body(b), TU(0), AScope(ScopeDecl), C(c), + DisplayedFunction(!displayProgress) { + setManagerCreators(); + } + + AnalysisManager(AnalysisConsumer& c, TranslationUnit* tu, + bool displayProgress) + : D(0), Body(0), TU(tu), AScope(ScopeTU), C(c), + DisplayedFunction(!displayProgress) { + setManagerCreators(); + } + + Decl* getCodeDecl() const { + assert (AScope == ScopeDecl); + return D; + } + + Stmt* getBody() const { + assert (AScope == ScopeDecl); + return Body; + } + + TranslationUnit* getTranslationUnit() const { + assert (AScope == ScopeTU); + return TU; + } + + StoreManagerCreator getStoreManagerCreator() { + return CreateStoreMgr; + }; + + ConstraintManagerCreator getConstraintManagerCreator() { + return CreateConstraintMgr; + } + + virtual CFG* getCFG() { + if (!cfg) cfg.reset(CFG::buildCFG(getBody())); + return cfg.get(); + } + + virtual ParentMap& getParentMap() { + if (!PM) + PM.reset(new ParentMap(getBody())); + return *PM.get(); + } + + virtual ASTContext& getContext() { + return *C.Ctx; + } + + virtual SourceManager& getSourceManager() { + return getContext().getSourceManager(); + } + + virtual Diagnostic& getDiagnostic() { + return C.Diags; + } + + const LangOptions& getLangOptions() const { + return C.LOpts; + } + + virtual PathDiagnosticClient* getPathDiagnosticClient() { + if (C.PD.get() == 0 && !C.OutDir.empty()) { + switch (AnalysisDiagOpt) { + default: +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE)\ +case PD_##NAME: C.PD.reset(CREATEFN(C.OutDir, C.PP, C.PPF)); break; +#include "Analyses.def" + } + } + return C.PD.get(); + } + + virtual LiveVariables* getLiveVariables() { + if (!liveness) { + CFG* c = getCFG(); + if (!c) return 0; + + liveness.reset(new LiveVariables(getContext(), *c)); + liveness->runOnCFG(*c); + liveness->runOnAllBlocks(*c, 0, true); + } + + return liveness.get(); + } + + bool shouldVisualizeGraphviz() const { return VisualizeEGDot; } + + bool shouldVisualizeUbigraph() const { return VisualizeEGUbi; } + + bool shouldVisualize() const { + return VisualizeEGDot || VisualizeEGUbi; + } + + bool shouldTrimGraph() const { return TrimGraph; } + + void DisplayFunction() { + + if (DisplayedFunction) + return; + + DisplayedFunction = true; + + // FIXME: Is getCodeDecl() always a named decl? + if (isa<FunctionDecl>(getCodeDecl()) || + isa<ObjCMethodDecl>(getCodeDecl())) { + NamedDecl *ND = cast<NamedDecl>(getCodeDecl()); + SourceManager &SM = getContext().getSourceManager(); + llvm::cerr << "ANALYZE: " + << SM.getPresumedLoc(ND->getLocation()).getFilename() + << ' ' << ND->getNameAsString() << '\n'; + } + } + + private: + /// Set configurable analyzer components creators. First check if there are + /// components registered at runtime. Otherwise fall back to builtin + /// components. + void setManagerCreators() { + if (ManagerRegistry::StoreMgrCreator != 0) { + CreateStoreMgr = ManagerRegistry::StoreMgrCreator; + } + else { + switch (AnalysisStoreOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateStoreMgr = CREATEFN; break; +#include "Analyses.def" + } + } + + if (ManagerRegistry::ConstraintMgrCreator != 0) + CreateConstraintMgr = ManagerRegistry::ConstraintMgrCreator; + else { + switch (AnalysisConstraintsOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateConstraintMgr = CREATEFN; break; +#include "Analyses.def" + } + } + + + // Some DiagnosticClients should be created all the time instead of + // lazily. Create those now. + switch (AnalysisDiagOpt) { + default: break; +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE)\ +case PD_##NAME: if (AUTOCREATE) getPathDiagnosticClient(); break; +#include "Analyses.def" + } + } + + }; + +} // end anonymous namespace + +namespace llvm { + template <> struct FoldingSetTrait<CodeAction> { + static inline void Profile(CodeAction X, FoldingSetNodeID& ID) { + ID.AddPointer(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(X))); + } + }; +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::HandleTopLevelDecl(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl* FD = cast<FunctionDecl>(D); + + if (AnalyzeSpecificFunction.size() > 0 && + AnalyzeSpecificFunction != FD->getIdentifier()->getName()) + break; + + Stmt* Body = FD->getBody(); + if (Body) HandleCode(FD, Body, FunctionActions); + break; + } + + case Decl::ObjCMethod: { + ObjCMethodDecl* MD = cast<ObjCMethodDecl>(D); + + if (AnalyzeSpecificFunction.size() > 0 && + AnalyzeSpecificFunction != MD->getSelector().getAsString()) + return; + + Stmt* Body = MD->getBody(); + if (Body) HandleCode(MD, Body, ObjCMethodActions); + break; + } + + default: + break; + } +} + +void AnalysisConsumer::HandleTranslationUnit(TranslationUnit& TU) { + + if(!TranslationUnitActions.empty()) { + AnalysisManager mgr(*this, &TU, AnalyzerDisplayProgress); + for (Actions::iterator I = TranslationUnitActions.begin(), + E = TranslationUnitActions.end(); I != E; ++I) + (*I)(mgr); + } + + if (!ObjCImplementationActions.empty()) + for (TranslationUnit::iterator I = TU.begin(), E = TU.end(); I!=E; ++I) + if (ObjCImplementationDecl* ID = dyn_cast<ObjCImplementationDecl>(*I)) + HandleCode(ID, 0, ObjCImplementationActions); + + // Delete the PathDiagnosticClient here just in case the AnalysisConsumer + // object doesn't get released. This will cause any side-effects in the + // destructor of the PathDiagnosticClient to get executed. + PD.reset(); +} + +void AnalysisConsumer::HandleCode(Decl* D, Stmt* Body, Actions& actions) { + + // Don't run the actions if an error has occured with parsing the file. + if (Diags.hasErrorOccurred()) + return; + + // Don't run the actions on declarations in header files unless + // otherwise specified. + if (!AnalyzeAll && !Ctx->getSourceManager().isFromMainFile(D->getLocation())) + return; + + // Create an AnalysisManager that will manage the state for analyzing + // this method/function. + AnalysisManager mgr(*this, D, Body, AnalyzerDisplayProgress); + + // Dispatch on the actions. + for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I) + (*I)(mgr); +} + +//===----------------------------------------------------------------------===// +// Analyses +//===----------------------------------------------------------------------===// + +static void ActionWarnDeadStores(AnalysisManager& mgr) { + if (LiveVariables* L = mgr.getLiveVariables()) { + BugReporter BR(mgr); + CheckDeadStores(*L, BR); + } +} + +static void ActionWarnUninitVals(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) + CheckUninitializedValues(*c, mgr.getContext(), mgr.getDiagnostic()); +} + + +static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf, + bool StandardWarnings = true) { + + + llvm::OwningPtr<GRTransferFuncs> TF(tf); + + // Display progress. + mgr.DisplayFunction(); + + // Construct the analysis engine. + LiveVariables* L = mgr.getLiveVariables(); + if (!L) return; + + GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L, mgr, + PurgeDead, EagerlyAssume, + mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator()); + + Eng.setTransferFunctions(tf); + + if (StandardWarnings) { + Eng.RegisterInternalChecks(); + RegisterAppleChecks(Eng); + } + + // Set the graph auditor. + llvm::OwningPtr<ExplodedNodeImpl::Auditor> Auditor; + if (mgr.shouldVisualizeUbigraph()) { + Auditor.reset(CreateUbiViz()); + ExplodedNodeImpl::SetAuditor(Auditor.get()); + } + + // Execute the worklist algorithm. + Eng.ExecuteWorkList(); + + // Release the auditor (if any) so that it doesn't monitor the graph + // created BugReporter. + ExplodedNodeImpl::SetAuditor(0); + + // Visualize the exploded graph. + if (mgr.shouldVisualizeGraphviz()) + Eng.ViewGraph(mgr.shouldTrimGraph()); + + // Display warnings. + Eng.getBugReporter().FlushReports(); +} + +static void ActionCheckerCFRefAux(AnalysisManager& mgr, bool GCEnabled, + bool StandardWarnings) { + + GRTransferFuncs* TF = MakeCFRefCountTF(mgr.getContext(), + GCEnabled, + mgr.getLangOptions()); + + ActionGRExprEngine(mgr, TF, StandardWarnings); +} + +static void ActionCheckerCFRef(AnalysisManager& mgr) { + + switch (mgr.getLangOptions().getGCMode()) { + default: + assert (false && "Invalid GC mode."); + case LangOptions::NonGC: + ActionCheckerCFRefAux(mgr, false, true); + break; + + case LangOptions::GCOnly: + ActionCheckerCFRefAux(mgr, true, true); + break; + + case LangOptions::HybridGC: + ActionCheckerCFRefAux(mgr, false, true); + ActionCheckerCFRefAux(mgr, true, false); + break; + } +} + +static void ActionCheckerSimple(AnalysisManager& mgr) { + ActionGRExprEngine(mgr, MakeGRSimpleValsTF()); +} + +static void ActionDisplayLiveVariables(AnalysisManager& mgr) { + if (LiveVariables* L = mgr.getLiveVariables()) { + mgr.DisplayFunction(); + L->dumpBlockLiveness(mgr.getSourceManager()); + } +} + +static void ActionCFGDump(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) { + mgr.DisplayFunction(); + c->dump(); + } +} + +static void ActionCFGView(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) { + mgr.DisplayFunction(); + c->viewCFG(); + } +} + +static void ActionWarnObjCDealloc(AnalysisManager& mgr) { + if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + + BugReporter BR(mgr); + + CheckObjCDealloc(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), + mgr.getLangOptions(), BR); +} + +static void ActionWarnObjCUnusedIvars(AnalysisManager& mgr) { + BugReporter BR(mgr); + CheckObjCUnusedIvar(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), BR); +} + +static void ActionWarnObjCMethSigs(AnalysisManager& mgr) { + BugReporter BR(mgr); + + CheckObjCInstMethSignature(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), + BR); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer creation. +//===----------------------------------------------------------------------===// + +ASTConsumer* clang::CreateAnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& OutDir) { + + llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(diags, pp, ppf, + lopts, OutDir)); + + for (unsigned i = 0; i < AnalysisList.size(); ++i) + switch (AnalysisList[i]) { +#define ANALYSIS(NAME, CMD, DESC, SCOPE)\ + case NAME:\ + C->add ## SCOPE ## Action(&Action ## NAME);\ + break; +#include "Analyses.def" + default: break; + } + + return C.take(); +} + +//===----------------------------------------------------------------------===// +// Ubigraph Visualization. FIXME: Move to separate file. +//===----------------------------------------------------------------------===// + +namespace { + +class UbigraphViz : public ExplodedNodeImpl::Auditor { + llvm::OwningPtr<llvm::raw_ostream> Out; + llvm::sys::Path Dir, Filename; + unsigned Cntr; + + typedef llvm::DenseMap<void*,unsigned> VMap; + VMap M; + +public: + UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename); + + ~UbigraphViz(); + + virtual void AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst); +}; + +} // end anonymous namespace + +static ExplodedNodeImpl::Auditor* CreateUbiViz() { + std::string ErrMsg; + + llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); + if (!ErrMsg.empty()) + return 0; + + llvm::sys::Path Filename = Dir; + Filename.appendComponent("llvm_ubi"); + Filename.makeUnique(true,&ErrMsg); + + if (!ErrMsg.empty()) + return 0; + + llvm::cerr << "Writing '" << Filename << "'.\n"; + + llvm::OwningPtr<llvm::raw_fd_ostream> Stream; + std::string filename = Filename.toString(); + Stream.reset(new llvm::raw_fd_ostream(filename.c_str(), false, ErrMsg)); + + if (!ErrMsg.empty()) + return 0; + + return new UbigraphViz(Stream.take(), Dir, Filename); +} + +void UbigraphViz::AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst) { + + assert (Src != Dst && "Self-edges are not allowed."); + + // Lookup the Src. If it is a new node, it's a root. + VMap::iterator SrcI= M.find(Src); + unsigned SrcID; + + if (SrcI == M.end()) { + M[Src] = SrcID = Cntr++; + *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; + } + else + SrcID = SrcI->second; + + // Lookup the Dst. + VMap::iterator DstI= M.find(Dst); + unsigned DstID; + + if (DstI == M.end()) { + M[Dst] = DstID = Cntr++; + *Out << "('vertex', " << DstID << ")\n"; + } + else { + // We have hit DstID before. Change its style to reflect a cache hit. + DstID = DstI->second; + *Out << "('change_vertex_style', " << DstID << ", 1)\n"; + } + + // Add the edge. + *Out << "('edge', " << SrcID << ", " << DstID + << ", ('arrow','true'), ('oriented', 'true'))\n"; +} + +UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename) + : Out(out), Dir(dir), Filename(filename), Cntr(0) { + + *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; + *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," + " ('size', '1.5'))\n"; +} + +UbigraphViz::~UbigraphViz() { + Out.reset(0); + llvm::cerr << "Running 'ubiviz' program... "; + std::string ErrMsg; + llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); + std::vector<const char*> args; + args.push_back(Ubiviz.c_str()); + args.push_back(Filename.c_str()); + args.push_back(0); + + if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { + llvm::cerr << "Error viewing graph: " << ErrMsg << "\n"; + } + + // Delete the directory. + Dir.eraseFromDisk(true); +} diff --git a/clang/tools/clang-cc/Backend.cpp b/clang/tools/clang-cc/Backend.cpp new file mode 100644 index 00000000000..459bd696b05 --- /dev/null +++ b/clang/tools/clang-cc/Backend.cpp @@ -0,0 +1,436 @@ +//===--- Backend.cpp - Interface to LLVM backend technologies -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/TranslationUnit.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompileOptions.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/PassManager.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Assembly/PrintModulePass.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/CodeGen/RegAllocRegistry.h" +#include "llvm/CodeGen/SchedulerRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Timer.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" +#include "llvm/Target/SubtargetFeature.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/IPO.h" +using namespace clang; +using namespace llvm; + +namespace { + class VISIBILITY_HIDDEN BackendConsumer : public ASTConsumer { + BackendAction Action; + CompileOptions CompileOpts; + const std::string &InputFile; + std::string OutputFile; + ASTContext *Context; + + Timer LLVMIRGeneration; + Timer CodeGenerationTime; + + llvm::OwningPtr<CodeGenerator> Gen; + + llvm::Module *TheModule; + llvm::TargetData *TheTargetData; + llvm::raw_ostream *AsmOutStream; + + mutable llvm::ModuleProvider *ModuleProvider; + mutable FunctionPassManager *CodeGenPasses; + mutable PassManager *PerModulePasses; + mutable FunctionPassManager *PerFunctionPasses; + + FunctionPassManager *getCodeGenPasses() const; + PassManager *getPerModulePasses() const; + FunctionPassManager *getPerFunctionPasses() const; + + void CreatePasses(); + + /// AddEmitPasses - Add passes necessary to emit assembly or LLVM + /// IR. + /// + /// \return True on success. On failure \arg Error will be set to + /// a user readable error message. + bool AddEmitPasses(std::string &Error); + + void EmitAssembly(); + + public: + BackendConsumer(BackendAction action, Diagnostic &Diags, + const LangOptions &langopts, const CompileOptions &compopts, + const std::string &infile, const std::string &outfile) : + Action(action), + CompileOpts(compopts), + InputFile(infile), + OutputFile(outfile), + LLVMIRGeneration("LLVM IR Generation Time"), + CodeGenerationTime("Code Generation Time"), + Gen(CreateLLVMCodeGen(Diags, langopts, InputFile, compopts.DebugInfo)), + TheModule(0), TheTargetData(0), AsmOutStream(0), ModuleProvider(0), + CodeGenPasses(0), PerModulePasses(0), PerFunctionPasses(0) { + + // Enable -time-passes if -ftime-report is enabled. + llvm::TimePassesIsEnabled = CompileOpts.TimePasses; + } + + ~BackendConsumer() { + delete AsmOutStream; + delete TheTargetData; + delete ModuleProvider; + delete CodeGenPasses; + delete PerModulePasses; + delete PerFunctionPasses; + } + + virtual void InitializeTU(TranslationUnit& TU) { + Context = &TU.getContext(); + + if (CompileOpts.TimePasses) + LLVMIRGeneration.startTimer(); + + Gen->InitializeTU(TU); + + TheModule = Gen->GetModule(); + ModuleProvider = new ExistingModuleProvider(TheModule); + TheTargetData = + new llvm::TargetData(TU.getContext().Target.getTargetDescription()); + + if (CompileOpts.TimePasses) + LLVMIRGeneration.stopTimer(); + } + + virtual void HandleTopLevelDecl(Decl *D) { + PrettyStackTraceDecl CrashInfo(D, SourceLocation(), + Context->getSourceManager(), + "LLVM IR generation of declaration"); + if (CompileOpts.TimePasses) + LLVMIRGeneration.startTimer(); + + Gen->HandleTopLevelDecl(D); + + if (CompileOpts.TimePasses) + LLVMIRGeneration.stopTimer(); + } + + virtual void HandleTranslationUnit(TranslationUnit& TU) { + { + PrettyStackTraceString CrashInfo("Per-file LLVM IR generation"); + if (CompileOpts.TimePasses) + LLVMIRGeneration.startTimer(); + + Gen->HandleTranslationUnit(TU); + + if (CompileOpts.TimePasses) + LLVMIRGeneration.stopTimer(); + } + + // EmitAssembly times and registers crash info itself. + EmitAssembly(); + + // Force a flush here in case we never get released. + if (AsmOutStream) + AsmOutStream->flush(); + } + + virtual void HandleTagDeclDefinition(TagDecl *D) { + PrettyStackTraceDecl CrashInfo(D, SourceLocation(), + Context->getSourceManager(), + "LLVM IR generation of declaration"); + Gen->HandleTagDeclDefinition(D); + } + }; +} + +FunctionPassManager *BackendConsumer::getCodeGenPasses() const { + if (!CodeGenPasses) { + CodeGenPasses = new FunctionPassManager(ModuleProvider); + CodeGenPasses->add(new TargetData(*TheTargetData)); + } + + return CodeGenPasses; +} + +PassManager *BackendConsumer::getPerModulePasses() const { + if (!PerModulePasses) { + PerModulePasses = new PassManager(); + PerModulePasses->add(new TargetData(*TheTargetData)); + } + + return PerModulePasses; +} + +FunctionPassManager *BackendConsumer::getPerFunctionPasses() const { + if (!PerFunctionPasses) { + PerFunctionPasses = new FunctionPassManager(ModuleProvider); + PerFunctionPasses->add(new TargetData(*TheTargetData)); + } + + return PerFunctionPasses; +} + +bool BackendConsumer::AddEmitPasses(std::string &Error) { + if (Action == Backend_EmitNothing) + return true; + + if (OutputFile == "-" || (InputFile == "-" && OutputFile.empty())) { + AsmOutStream = new raw_stdout_ostream(); + sys::Program::ChangeStdoutToBinary(); + } else { + if (OutputFile.empty()) { + llvm::sys::Path Path(InputFile); + Path.eraseSuffix(); + if (Action == Backend_EmitBC) { + Path.appendSuffix("bc"); + } else if (Action == Backend_EmitLL) { + Path.appendSuffix("ll"); + } else { + Path.appendSuffix("s"); + } + OutputFile = Path.toString(); + } + + AsmOutStream = new raw_fd_ostream(OutputFile.c_str(), true, Error); + if (!Error.empty()) + return false; + } + + if (Action == Backend_EmitBC) { + getPerModulePasses()->add(createBitcodeWriterPass(*AsmOutStream)); + } else if (Action == Backend_EmitLL) { + getPerModulePasses()->add(createPrintModulePass(AsmOutStream)); + } else { + bool Fast = CompileOpts.OptimizationLevel == 0; + + // Create the TargetMachine for generating code. + const TargetMachineRegistry::entry *TME = + TargetMachineRegistry::getClosestStaticTargetForModule(*TheModule, Error); + if (!TME) { + Error = std::string("Unable to get target machine: ") + Error; + return false; + } + + std::string FeaturesStr; + if (CompileOpts.CPU.size() || CompileOpts.Features.size()) { + SubtargetFeatures Features; + Features.setCPU(CompileOpts.CPU); + for (std::vector<std::string>::iterator + it = CompileOpts.Features.begin(), + ie = CompileOpts.Features.end(); it != ie; ++it) + Features.AddFeature(*it); + FeaturesStr = Features.getString(); + } + TargetMachine *TM = TME->CtorFn(*TheModule, FeaturesStr); + + // Set register scheduler & allocation policy. + RegisterScheduler::setDefault(createDefaultScheduler); + RegisterRegAlloc::setDefault(Fast ? createLocalRegisterAllocator : + createLinearScanRegisterAllocator); + + // From llvm-gcc: + // If there are passes we have to run on the entire module, we do codegen + // as a separate "pass" after that happens. + // FIXME: This is disabled right now until bugs can be worked out. Reenable + // this for fast -O0 compiles! + FunctionPassManager *PM = getCodeGenPasses(); + + // Normal mode, emit a .s file by running the code generator. + // Note, this also adds codegenerator level optimization passes. + switch (TM->addPassesToEmitFile(*PM, *AsmOutStream, + TargetMachine::AssemblyFile, Fast)) { + default: + case FileModel::Error: + Error = "Unable to interface with target machine!\n"; + return false; + case FileModel::AsmFile: + break; + } + + if (TM->addPassesToEmitFileFinish(*CodeGenPasses, 0, Fast)) { + Error = "Unable to interface with target machine!\n"; + return false; + } + } + + return true; +} + +void BackendConsumer::CreatePasses() { + // In -O0 if checking is disabled, we don't even have per-function passes. + if (CompileOpts.VerifyModule) + getPerFunctionPasses()->add(createVerifierPass()); + + if (CompileOpts.OptimizationLevel > 0) { + FunctionPassManager *PM = getPerFunctionPasses(); + PM->add(createCFGSimplificationPass()); + if (CompileOpts.OptimizationLevel == 1) + PM->add(createPromoteMemoryToRegisterPass()); + else + PM->add(createScalarReplAggregatesPass()); + PM->add(createInstructionCombiningPass()); + } + + // For now we always create per module passes. + PassManager *PM = getPerModulePasses(); + if (CompileOpts.OptimizationLevel > 0) { + if (CompileOpts.UnitAtATime) + PM->add(createRaiseAllocationsPass()); // call %malloc -> malloc inst + PM->add(createCFGSimplificationPass()); // Clean up disgusting code + PM->add(createPromoteMemoryToRegisterPass()); // Kill useless allocas + if (CompileOpts.UnitAtATime) { + PM->add(createGlobalOptimizerPass()); // Optimize out global vars + PM->add(createGlobalDCEPass()); // Remove unused fns and globs + PM->add(createIPConstantPropagationPass()); // IP Constant Propagation + PM->add(createDeadArgEliminationPass()); // Dead argument elimination + } + PM->add(createInstructionCombiningPass()); // Clean up after IPCP & DAE + PM->add(createCFGSimplificationPass()); // Clean up after IPCP & DAE + if (CompileOpts.UnitAtATime) { + PM->add(createPruneEHPass()); // Remove dead EH info + PM->add(createFunctionAttrsPass()); // Set readonly/readnone attrs + } + if (CompileOpts.InlineFunctions) + PM->add(createFunctionInliningPass()); // Inline small functions + else + PM->add(createAlwaysInlinerPass()); // Respect always_inline + if (CompileOpts.OptimizationLevel > 2) + PM->add(createArgumentPromotionPass()); // Scalarize uninlined fn args + if (CompileOpts.SimplifyLibCalls) + PM->add(createSimplifyLibCallsPass()); // Library Call Optimizations + PM->add(createInstructionCombiningPass()); // Cleanup for scalarrepl. + PM->add(createJumpThreadingPass()); // Thread jumps. + PM->add(createCFGSimplificationPass()); // Merge & remove BBs + PM->add(createScalarReplAggregatesPass()); // Break up aggregate allocas + PM->add(createInstructionCombiningPass()); // Combine silly seq's + PM->add(createCondPropagationPass()); // Propagate conditionals + PM->add(createTailCallEliminationPass()); // Eliminate tail calls + PM->add(createCFGSimplificationPass()); // Merge & remove BBs + PM->add(createReassociatePass()); // Reassociate expressions + PM->add(createLoopRotatePass()); // Rotate Loop + PM->add(createLICMPass()); // Hoist loop invariants + PM->add(createLoopUnswitchPass(CompileOpts.OptimizeSize ? true : false)); +// PM->add(createLoopIndexSplitPass()); // Split loop index + PM->add(createInstructionCombiningPass()); + PM->add(createIndVarSimplifyPass()); // Canonicalize indvars + PM->add(createLoopDeletionPass()); // Delete dead loops + if (CompileOpts.UnrollLoops) + PM->add(createLoopUnrollPass()); // Unroll small loops + PM->add(createInstructionCombiningPass()); // Clean up after the unroller + PM->add(createGVNPass()); // Remove redundancies + PM->add(createMemCpyOptPass()); // Remove memcpy / form memset + PM->add(createSCCPPass()); // Constant prop with SCCP + + // Run instcombine after redundancy elimination to exploit opportunities + // opened up by them. + PM->add(createInstructionCombiningPass()); + PM->add(createCondPropagationPass()); // Propagate conditionals + PM->add(createDeadStoreEliminationPass()); // Delete dead stores + PM->add(createAggressiveDCEPass()); // Delete dead instructions + PM->add(createCFGSimplificationPass()); // Merge & remove BBs + + if (CompileOpts.UnitAtATime) { + PM->add(createStripDeadPrototypesPass()); // Get rid of dead prototypes + PM->add(createDeadTypeEliminationPass()); // Eliminate dead types + } + + if (CompileOpts.OptimizationLevel > 1 && CompileOpts.UnitAtATime) + PM->add(createConstantMergePass()); // Merge dup global constants + } else { + PM->add(createAlwaysInlinerPass()); + } +} + +/// EmitAssembly - Handle interaction with LLVM backend to generate +/// actual machine code. +void BackendConsumer::EmitAssembly() { + // Silently ignore if we weren't initialized for some reason. + if (!TheModule || !TheTargetData) + return; + + + TimeRegion Region(CompileOpts.TimePasses ? &CodeGenerationTime : 0); + + // Make sure IR generation is happy with the module. This is + // released by the module provider. + Module *M = Gen->ReleaseModule(); + if (!M) { + // The module has been released by IR gen on failures, do not + // double free. + ModuleProvider->releaseModule(); + TheModule = 0; + return; + } + + assert(TheModule == M && "Unexpected module change during IR generation"); + + CreatePasses(); + + std::string Error; + if (!AddEmitPasses(Error)) { + // FIXME: Don't fail this way. + llvm::cerr << "ERROR: " << Error << "\n"; + ::exit(1); + } + + // Run passes. For now we do all passes at once, but eventually we + // would like to have the option of streaming code generation. + + if (PerFunctionPasses) { + PrettyStackTraceString CrashInfo("Per-function optimization"); + + PerFunctionPasses->doInitialization(); + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + if (!I->isDeclaration()) + PerFunctionPasses->run(*I); + PerFunctionPasses->doFinalization(); + } + + if (PerModulePasses) { + PrettyStackTraceString CrashInfo("Per-module optimization passes"); + PerModulePasses->run(*M); + } + + if (CodeGenPasses) { + PrettyStackTraceString CrashInfo("Code generation"); + CodeGenPasses->doInitialization(); + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) + if (!I->isDeclaration()) + CodeGenPasses->run(*I); + CodeGenPasses->doFinalization(); + } +} + +ASTConsumer *clang::CreateBackendConsumer(BackendAction Action, + Diagnostic &Diags, + const LangOptions &LangOpts, + const CompileOptions &CompileOpts, + const std::string& InFile, + const std::string& OutFile) { + // FIXME: If optimizing, disable all debug info generation. The LLVM + // optimizer and backend is not ready to handle it when optimizations + // are enabled. + if (CompileOpts.OptimizationLevel > 0) + const_cast<CompileOptions&>(CompileOpts).DebugInfo = false; + + return new BackendConsumer(Action, Diags, LangOpts, CompileOpts, + InFile, OutFile); +} diff --git a/clang/tools/clang-cc/CMakeLists.txt b/clang/tools/clang-cc/CMakeLists.txt new file mode 100644 index 00000000000..9dd596c2ce1 --- /dev/null +++ b/clang/tools/clang-cc/CMakeLists.txt @@ -0,0 +1,41 @@ +set(LLVM_NO_RTTI 1) + +set( LLVM_USED_LIBS + clangCodeGen + clangAnalysis + clangRewrite + clangSema + clangDriver + clangAST + clangParse + clangLex + clangBasic + ) + +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + bitreader + bitwriter + codegen + ipo + selectiondag + ) + +add_clang_executable(clang-cc + AnalysisConsumer.cpp + ASTConsumers.cpp + Backend.cpp + CacheTokens.cpp + clang.cpp + DependencyFile.cpp + DiagChecker.cpp + HTMLPrint.cpp + PrintParserCallbacks.cpp + PrintPreprocessedOutput.cpp + RewriteBlocks.cpp + RewriteMacros.cpp + RewriteObjC.cpp + RewriteTest.cpp + SerializationTest.cpp + Warnings.cpp + ) diff --git a/clang/tools/clang-cc/CacheTokens.cpp b/clang/tools/clang-cc/CacheTokens.cpp new file mode 100644 index 00000000000..4ca350e6fb0 --- /dev/null +++ b/clang/tools/clang-cc/CacheTokens.cpp @@ -0,0 +1,824 @@ +//===--- CacheTokens.cpp - Caching of lexer tokens for PTH support --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This provides a possible implementation of PTH support for Clang that is +// based on caching lexed tokens and identifiers. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Streams.h" + +// FIXME: put this somewhere else? +#ifndef S_ISDIR +#define S_ISDIR(x) (((x)&_S_IFDIR)!=0) +#endif + +using namespace clang; + +typedef uint32_t Offset; + +static void Emit8(llvm::raw_ostream& Out, uint32_t V) { + Out << (unsigned char)(V); +} + +static void Emit16(llvm::raw_ostream& Out, uint32_t V) { + Out << (unsigned char)(V); + Out << (unsigned char)(V >> 8); + assert((V >> 16) == 0); +} + +static void Emit32(llvm::raw_ostream& Out, uint32_t V) { + Out << (unsigned char)(V); + Out << (unsigned char)(V >> 8); + Out << (unsigned char)(V >> 16); + Out << (unsigned char)(V >> 24); +} + +static void Emit64(llvm::raw_ostream& Out, uint64_t V) { + Out << (unsigned char)(V); + Out << (unsigned char)(V >> 8); + Out << (unsigned char)(V >> 16); + Out << (unsigned char)(V >> 24); + Out << (unsigned char)(V >> 32); + Out << (unsigned char)(V >> 40); + Out << (unsigned char)(V >> 48); + Out << (unsigned char)(V >> 56); +} + +static void Pad(llvm::raw_fd_ostream& Out, unsigned A) { + Offset off = (Offset) Out.tell(); + uint32_t n = ((uintptr_t)(off+A-1) & ~(uintptr_t)(A-1)) - off; + for ( ; n ; --n ) Emit8(Out, 0); +} + +// Bernstein hash function: +// This is basically copy-and-paste from StringMap. This likely won't +// stay here, which is why I didn't both to expose this function from +// String Map. +static unsigned BernsteinHash(const char* x) { + unsigned int R = 0; + for ( ; *x != '\0' ; ++x) R = R * 33 + *x; + return R + (R >> 5); +} + +//===----------------------------------------------------------------------===// +// On Disk Hashtable Logic. This will eventually get refactored and put +// elsewhere. +//===----------------------------------------------------------------------===// + +template<typename Info> +class OnDiskChainedHashTableGenerator { + unsigned NumBuckets; + unsigned NumEntries; + llvm::BumpPtrAllocator BA; + + class Item { + public: + typename Info::key_type key; + typename Info::data_type data; + Item *next; + const uint32_t hash; + + Item(typename Info::key_type_ref k, typename Info::data_type_ref d) + : key(k), data(d), next(0), hash(Info::ComputeHash(k)) {} + }; + + class Bucket { + public: + Offset off; + Item* head; + unsigned length; + + Bucket() {} + }; + + Bucket* Buckets; + +private: + void insert(Bucket* b, size_t size, Item* E) { + unsigned idx = E->hash & (size - 1); + Bucket& B = b[idx]; + E->next = B.head; + ++B.length; + B.head = E; + } + + void resize(size_t newsize) { + Bucket* newBuckets = (Bucket*) calloc(newsize, sizeof(Bucket)); + // Populate newBuckets with the old entries. + for (unsigned i = 0; i < NumBuckets; ++i) + for (Item* E = Buckets[i].head; E ; ) { + Item* N = E->next; + E->next = 0; + insert(newBuckets, newsize, E); + E = N; + } + + free(Buckets); + NumBuckets = newsize; + Buckets = newBuckets; + } + +public: + + void insert(typename Info::key_type_ref key, + typename Info::data_type_ref data) { + + ++NumEntries; + if (4*NumEntries >= 3*NumBuckets) resize(NumBuckets*2); + insert(Buckets, NumBuckets, new (BA.Allocate<Item>()) Item(key, data)); + } + + Offset Emit(llvm::raw_fd_ostream& out) { + // Emit the payload of the table. + for (unsigned i = 0; i < NumBuckets; ++i) { + Bucket& B = Buckets[i]; + if (!B.head) continue; + + // Store the offset for the data of this bucket. + B.off = out.tell(); + + // Write out the number of items in the bucket. + Emit16(out, B.length); + + // Write out the entries in the bucket. + for (Item *I = B.head; I ; I = I->next) { + Emit32(out, I->hash); + const std::pair<unsigned, unsigned>& Len = + Info::EmitKeyDataLength(out, I->key, I->data); + Info::EmitKey(out, I->key, Len.first); + Info::EmitData(out, I->key, I->data, Len.second); + } + } + + // Emit the hashtable itself. + Pad(out, 4); + Offset TableOff = out.tell(); + Emit32(out, NumBuckets); + Emit32(out, NumEntries); + for (unsigned i = 0; i < NumBuckets; ++i) Emit32(out, Buckets[i].off); + + return TableOff; + } + + OnDiskChainedHashTableGenerator() { + NumEntries = 0; + NumBuckets = 64; + // Note that we do not need to run the constructors of the individual + // Bucket objects since 'calloc' returns bytes that are all 0. + Buckets = (Bucket*) calloc(NumBuckets, sizeof(Bucket)); + } + + ~OnDiskChainedHashTableGenerator() { + free(Buckets); + } +}; + +//===----------------------------------------------------------------------===// +// PTH-specific stuff. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN PTHEntry { + Offset TokenData, PPCondData; + +public: + PTHEntry() {} + + PTHEntry(Offset td, Offset ppcd) + : TokenData(td), PPCondData(ppcd) {} + + Offset getTokenOffset() const { return TokenData; } + Offset getPPCondTableOffset() const { return PPCondData; } +}; + + +class VISIBILITY_HIDDEN PTHEntryKeyVariant { + union { const FileEntry* FE; const char* Path; }; + enum { IsFE = 0x1, IsDE = 0x2, IsNoExist = 0x0 } Kind; + struct stat *StatBuf; +public: + PTHEntryKeyVariant(const FileEntry *fe) + : FE(fe), Kind(IsFE), StatBuf(0) {} + + PTHEntryKeyVariant(struct stat* statbuf, const char* path) + : Path(path), Kind(IsDE), StatBuf(new struct stat(*statbuf)) {} + + PTHEntryKeyVariant(const char* path) + : Path(path), Kind(IsNoExist), StatBuf(0) {} + + bool isFile() const { return Kind == IsFE; } + + const char* getCString() const { + return Kind == IsFE ? FE->getName() : Path; + } + + unsigned getKind() const { return (unsigned) Kind; } + + void EmitData(llvm::raw_ostream& Out) { + switch (Kind) { + case IsFE: + // Emit stat information. + ::Emit32(Out, FE->getInode()); + ::Emit32(Out, FE->getDevice()); + ::Emit16(Out, FE->getFileMode()); + ::Emit64(Out, FE->getModificationTime()); + ::Emit64(Out, FE->getSize()); + break; + case IsDE: + // Emit stat information. + ::Emit32(Out, (uint32_t) StatBuf->st_ino); + ::Emit32(Out, (uint32_t) StatBuf->st_dev); + ::Emit16(Out, (uint16_t) StatBuf->st_mode); + ::Emit64(Out, (uint64_t) StatBuf->st_mtime); + ::Emit64(Out, (uint64_t) StatBuf->st_size); + delete StatBuf; + break; + default: + break; + } + } + + unsigned getRepresentationLength() const { + return Kind == IsNoExist ? 0 : 4 + 4 + 2 + 8 + 8; + } +}; + +class VISIBILITY_HIDDEN FileEntryPTHEntryInfo { +public: + typedef PTHEntryKeyVariant key_type; + typedef key_type key_type_ref; + + typedef PTHEntry data_type; + typedef const PTHEntry& data_type_ref; + + static unsigned ComputeHash(PTHEntryKeyVariant V) { + return BernsteinHash(V.getCString()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(llvm::raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E) { + + unsigned n = strlen(V.getCString()) + 1 + 1; + ::Emit16(Out, n); + + unsigned m = V.getRepresentationLength() + (V.isFile() ? 4 + 4 : 0); + ::Emit8(Out, m); + + return std::make_pair(n, m); + } + + static void EmitKey(llvm::raw_ostream& Out, PTHEntryKeyVariant V, unsigned n){ + // Emit the entry kind. + ::Emit8(Out, (unsigned) V.getKind()); + // Emit the string. + Out.write(V.getCString(), n - 1); + } + + static void EmitData(llvm::raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E, unsigned) { + + + // For file entries emit the offsets into the PTH file for token data + // and the preprocessor blocks table. + if (V.isFile()) { + ::Emit32(Out, E.getTokenOffset()); + ::Emit32(Out, E.getPPCondTableOffset()); + } + + // Emit any other data associated with the key (i.e., stat information). + V.EmitData(Out); + } +}; + +class OffsetOpt { + bool valid; + Offset off; +public: + OffsetOpt() : valid(false) {} + bool hasOffset() const { return valid; } + Offset getOffset() const { assert(valid); return off; } + void setOffset(Offset o) { off = o; valid = true; } +}; +} // end anonymous namespace + +typedef OnDiskChainedHashTableGenerator<FileEntryPTHEntryInfo> PTHMap; +typedef llvm::DenseMap<const IdentifierInfo*,uint32_t> IDMap; +typedef llvm::StringMap<OffsetOpt, llvm::BumpPtrAllocator> CachedStrsTy; + +namespace { +class VISIBILITY_HIDDEN PTHWriter { + IDMap IM; + llvm::raw_fd_ostream& Out; + Preprocessor& PP; + uint32_t idcount; + PTHMap PM; + CachedStrsTy CachedStrs; + Offset CurStrOffset; + std::vector<llvm::StringMapEntry<OffsetOpt>*> StrEntries; + + //// Get the persistent id for the given IdentifierInfo*. + uint32_t ResolveID(const IdentifierInfo* II); + + /// Emit a token to the PTH file. + void EmitToken(const Token& T); + + void Emit8(uint32_t V) { + Out << (unsigned char)(V); + } + + void Emit16(uint32_t V) { ::Emit16(Out, V); } + + void Emit24(uint32_t V) { + Out << (unsigned char)(V); + Out << (unsigned char)(V >> 8); + Out << (unsigned char)(V >> 16); + assert((V >> 24) == 0); + } + + void Emit32(uint32_t V) { ::Emit32(Out, V); } + + void EmitBuf(const char* I, const char* E) { + for ( ; I != E ; ++I) Out << *I; + } + + /// EmitIdentifierTable - Emits two tables to the PTH file. The first is + /// a hashtable mapping from identifier strings to persistent IDs. + /// The second is a straight table mapping from persistent IDs to string data + /// (the keys of the first table). + std::pair<Offset, Offset> EmitIdentifierTable(); + + /// EmitFileTable - Emit a table mapping from file name strings to PTH + /// token data. + Offset EmitFileTable() { return PM.Emit(Out); } + + PTHEntry LexTokens(Lexer& L); + Offset EmitCachedSpellings(); + + /// StatListener - A simple "interpose" object used to monitor stat calls + /// invoked by FileManager while processing the original sources used + /// as input to PTH generation. StatListener populates the PTHWriter's + /// file map with stat information for directories as well as negative stats. + /// Stat information for files are populated elsewhere. + class StatListener : public StatSysCallCache { + PTHMap& PM; + public: + StatListener(PTHMap& pm) : PM(pm) {} + ~StatListener() {} + + int stat(const char *path, struct stat *buf) { + int result = ::stat(path, buf); + + if (result != 0) // Failed 'stat'. + PM.insert(path, PTHEntry()); + else if (S_ISDIR(buf->st_mode)) { + // Only cache directories with absolute paths. + if (!llvm::sys::Path(path).isAbsolute()) + return result; + + PM.insert(PTHEntryKeyVariant(buf, path), PTHEntry()); + } + + return result; + } + }; + +public: + PTHWriter(llvm::raw_fd_ostream& out, Preprocessor& pp) + : Out(out), PP(pp), idcount(0), CurStrOffset(0) {} + + void GeneratePTH(const std::string *MainFile = 0); + + StatSysCallCache *createStatListener() { + return new StatListener(PM); + } +}; +} // end anonymous namespace + +uint32_t PTHWriter::ResolveID(const IdentifierInfo* II) { + // Null IdentifierInfo's map to the persistent ID 0. + if (!II) + return 0; + + IDMap::iterator I = IM.find(II); + + if (I == IM.end()) { + IM[II] = ++idcount; // Pre-increment since '0' is reserved for NULL. + return idcount; + } + + return I->second; // We've already added 1. +} + +void PTHWriter::EmitToken(const Token& T) { + // Emit the token kind, flags, and length. + Emit32(((uint32_t) T.getKind()) | ((((uint32_t) T.getFlags())) << 8)| + (((uint32_t) T.getLength()) << 16)); + + if (T.isLiteral()) { + // We cache *un-cleaned* spellings. This gives us 100% fidelity with the + // source code. + const char* s = T.getLiteralData(); + unsigned len = T.getLength(); + + // Get the string entry. + llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s, s+len); + + // If this is a new string entry, bump the PTH offset. + if (!E->getValue().hasOffset()) { + E->getValue().setOffset(CurStrOffset); + StrEntries.push_back(E); + CurStrOffset += len + 1; + } + + // Emit the relative offset into the PTH file for the spelling string. + Emit32(E->getValue().getOffset()); + } + else + Emit32(ResolveID(T.getIdentifierInfo())); + + // Emit the offset into the original source file of this token so that we + // can reconstruct its SourceLocation. + Emit32(PP.getSourceManager().getFileOffset(T.getLocation())); +} + +PTHEntry PTHWriter::LexTokens(Lexer& L) { + // Pad 0's so that we emit tokens to a 4-byte alignment. + // This speed up reading them back in. + Pad(Out, 4); + Offset off = (Offset) Out.tell(); + + // Keep track of matching '#if' ... '#endif'. + typedef std::vector<std::pair<Offset, unsigned> > PPCondTable; + PPCondTable PPCond; + std::vector<unsigned> PPStartCond; + bool ParsingPreprocessorDirective = false; + Token Tok; + + do { + L.LexFromRawLexer(Tok); + NextToken: + + if ((Tok.isAtStartOfLine() || Tok.is(tok::eof)) && + ParsingPreprocessorDirective) { + // Insert an eom token into the token cache. It has the same + // position as the next token that is not on the same line as the + // preprocessor directive. Observe that we continue processing + // 'Tok' when we exit this branch. + Token Tmp = Tok; + Tmp.setKind(tok::eom); + Tmp.clearFlag(Token::StartOfLine); + Tmp.setIdentifierInfo(0); + EmitToken(Tmp); + ParsingPreprocessorDirective = false; + } + + if (Tok.is(tok::identifier)) { + Tok.setIdentifierInfo(PP.LookUpIdentifierInfo(Tok)); + EmitToken(Tok); + continue; + } + + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { + // Special processing for #include. Store the '#' token and lex + // the next token. + assert(!ParsingPreprocessorDirective); + Offset HashOff = (Offset) Out.tell(); + EmitToken(Tok); + + // Get the next token. + L.LexFromRawLexer(Tok); + + assert(!Tok.isAtStartOfLine()); + + // Did we see 'include'/'import'/'include_next'? + if (!Tok.is(tok::identifier)) { + EmitToken(Tok); + continue; + } + + IdentifierInfo* II = PP.LookUpIdentifierInfo(Tok); + Tok.setIdentifierInfo(II); + tok::PPKeywordKind K = II->getPPKeywordID(); + + assert(K != tok::pp_not_keyword); + ParsingPreprocessorDirective = true; + + switch (K) { + default: + break; + case tok::pp_include: + case tok::pp_import: + case tok::pp_include_next: { + // Save the 'include' token. + EmitToken(Tok); + // Lex the next token as an include string. + L.setParsingPreprocessorDirective(true); + L.LexIncludeFilename(Tok); + L.setParsingPreprocessorDirective(false); + assert(!Tok.isAtStartOfLine()); + if (Tok.is(tok::identifier)) + Tok.setIdentifierInfo(PP.LookUpIdentifierInfo(Tok)); + + break; + } + case tok::pp_if: + case tok::pp_ifdef: + case tok::pp_ifndef: { + // Add an entry for '#if' and friends. We initially set the target + // index to 0. This will get backpatched when we hit #endif. + PPStartCond.push_back(PPCond.size()); + PPCond.push_back(std::make_pair(HashOff, 0U)); + break; + } + case tok::pp_endif: { + // Add an entry for '#endif'. We set the target table index to itself. + // This will later be set to zero when emitting to the PTH file. We + // use 0 for uninitialized indices because that is easier to debug. + unsigned index = PPCond.size(); + // Backpatch the opening '#if' entry. + assert(!PPStartCond.empty()); + assert(PPCond.size() > PPStartCond.back()); + assert(PPCond[PPStartCond.back()].second == 0); + PPCond[PPStartCond.back()].second = index; + PPStartCond.pop_back(); + // Add the new entry to PPCond. + PPCond.push_back(std::make_pair(HashOff, index)); + EmitToken(Tok); + + // Some files have gibberish on the same line as '#endif'. + // Discard these tokens. + do L.LexFromRawLexer(Tok); while (!Tok.is(tok::eof) && + !Tok.isAtStartOfLine()); + // We have the next token in hand. + // Don't immediately lex the next one. + goto NextToken; + } + case tok::pp_elif: + case tok::pp_else: { + // Add an entry for #elif or #else. + // This serves as both a closing and opening of a conditional block. + // This means that its entry will get backpatched later. + unsigned index = PPCond.size(); + // Backpatch the previous '#if' entry. + assert(!PPStartCond.empty()); + assert(PPCond.size() > PPStartCond.back()); + assert(PPCond[PPStartCond.back()].second == 0); + PPCond[PPStartCond.back()].second = index; + PPStartCond.pop_back(); + // Now add '#elif' as a new block opening. + PPCond.push_back(std::make_pair(HashOff, 0U)); + PPStartCond.push_back(index); + break; + } + } + } + + EmitToken(Tok); + } + while (Tok.isNot(tok::eof)); + + assert(PPStartCond.empty() && "Error: imblanced preprocessor conditionals."); + + // Next write out PPCond. + Offset PPCondOff = (Offset) Out.tell(); + + // Write out the size of PPCond so that clients can identifer empty tables. + Emit32(PPCond.size()); + + for (unsigned i = 0, e = PPCond.size(); i!=e; ++i) { + Emit32(PPCond[i].first - off); + uint32_t x = PPCond[i].second; + assert(x != 0 && "PPCond entry not backpatched."); + // Emit zero for #endifs. This allows us to do checking when + // we read the PTH file back in. + Emit32(x == i ? 0 : x); + } + + return PTHEntry(off, PPCondOff); +} + +Offset PTHWriter::EmitCachedSpellings() { + // Write each cached strings to the PTH file. + Offset SpellingsOff = Out.tell(); + + for (std::vector<llvm::StringMapEntry<OffsetOpt>*>::iterator + I = StrEntries.begin(), E = StrEntries.end(); I!=E; ++I) { + + const char* data = (*I)->getKeyData(); + EmitBuf(data, data + (*I)->getKeyLength()); + Emit8('\0'); + } + + return SpellingsOff; +} + +void PTHWriter::GeneratePTH(const std::string *MainFile) { + // Generate the prologue. + Out << "cfe-pth"; + Emit32(PTHManager::Version); + + // Leave 4 words for the prologue. + Offset PrologueOffset = Out.tell(); + for (unsigned i = 0; i < 4 * sizeof(uint32_t); ++i) Emit8(0); + + // Write the name of the MainFile. + if (MainFile && MainFile->length() > 0) { + Emit16(MainFile->length()); + EmitBuf(&((*MainFile)[0]), &((*MainFile)[0]) + MainFile->length()); + } + else { + // String with 0 bytes. + Emit16(0); + } + Emit8(0); + + // Iterate over all the files in SourceManager. Create a lexer + // for each file and cache the tokens. + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LOpts = PP.getLangOptions(); + + for (SourceManager::fileinfo_iterator I = SM.fileinfo_begin(), + E = SM.fileinfo_end(); I != E; ++I) { + const SrcMgr::ContentCache &C = *I->second; + const FileEntry *FE = C.Entry; + + // FIXME: Handle files with non-absolute paths. + llvm::sys::Path P(FE->getName()); + if (!P.isAbsolute()) + continue; + + const llvm::MemoryBuffer *B = C.getBuffer(); + if (!B) continue; + + FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); + Lexer L(FID, SM, LOpts); + PM.insert(FE, LexTokens(L)); + } + + // Write out the identifier table. + const std::pair<Offset,Offset>& IdTableOff = EmitIdentifierTable(); + + // Write out the cached strings table. + Offset SpellingOff = EmitCachedSpellings(); + + // Write out the file table. + Offset FileTableOff = EmitFileTable(); + + // Finally, write the prologue. + Out.seek(PrologueOffset); + Emit32(IdTableOff.first); + Emit32(IdTableOff.second); + Emit32(FileTableOff); + Emit32(SpellingOff); +} + +void clang::CacheTokens(Preprocessor& PP, const std::string& OutFile) { + // Open up the PTH file. + std::string ErrMsg; + llvm::raw_fd_ostream Out(OutFile.c_str(), true, ErrMsg); + + if (!ErrMsg.empty()) { + llvm::errs() << "PTH error: " << ErrMsg << "\n"; + return; + } + + // Get the name of the main file. + const SourceManager &SrcMgr = PP.getSourceManager(); + const FileEntry *MainFile = SrcMgr.getFileEntryForID(SrcMgr.getMainFileID()); + llvm::sys::Path MainFilePath(MainFile->getName()); + std::string MainFileName; + + if (!MainFilePath.isAbsolute()) { + llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory(); + P.appendComponent(MainFilePath.toString()); + MainFileName = P.toString(); + } + else { + MainFileName = MainFilePath.toString(); + } + + // Create the PTHWriter. + PTHWriter PW(Out, PP); + + // Install the 'stat' system call listener in the FileManager. + PP.getFileManager().setStatCache(PW.createStatListener()); + + // Lex through the entire file. This will populate SourceManager with + // all of the header information. + Token Tok; + PP.EnterMainSourceFile(); + do { PP.Lex(Tok); } while (Tok.isNot(tok::eof)); + + // Generate the PTH file. + PP.getFileManager().setStatCache(0); + PW.GeneratePTH(&MainFileName); +} + +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN PTHIdKey { +public: + const IdentifierInfo* II; + uint32_t FileOffset; +}; + +class VISIBILITY_HIDDEN PTHIdentifierTableTrait { +public: + typedef PTHIdKey* key_type; + typedef key_type key_type_ref; + + typedef uint32_t data_type; + typedef data_type data_type_ref; + + static unsigned ComputeHash(PTHIdKey* key) { + return BernsteinHash(key->II->getName()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(llvm::raw_ostream& Out, const PTHIdKey* key, uint32_t) { + unsigned n = strlen(key->II->getName()) + 1; + ::Emit16(Out, n); + return std::make_pair(n, sizeof(uint32_t)); + } + + static void EmitKey(llvm::raw_fd_ostream& Out, PTHIdKey* key, unsigned n) { + // Record the location of the key data. This is used when generating + // the mapping from persistent IDs to strings. + key->FileOffset = Out.tell(); + Out.write(key->II->getName(), n); + } + + static void EmitData(llvm::raw_ostream& Out, PTHIdKey*, uint32_t pID, + unsigned) { + ::Emit32(Out, pID); + } +}; +} // end anonymous namespace + +/// EmitIdentifierTable - Emits two tables to the PTH file. The first is +/// a hashtable mapping from identifier strings to persistent IDs. The second +/// is a straight table mapping from persistent IDs to string data (the +/// keys of the first table). +/// +std::pair<Offset,Offset> PTHWriter::EmitIdentifierTable() { + // Build two maps: + // (1) an inverse map from persistent IDs -> (IdentifierInfo*,Offset) + // (2) a map from (IdentifierInfo*, Offset)* -> persistent IDs + + // Note that we use 'calloc', so all the bytes are 0. + PTHIdKey* IIDMap = (PTHIdKey*) calloc(idcount, sizeof(PTHIdKey)); + + // Create the hashtable. + OnDiskChainedHashTableGenerator<PTHIdentifierTableTrait> IIOffMap; + + // Generate mapping from persistent IDs -> IdentifierInfo*. + for (IDMap::iterator I=IM.begin(), E=IM.end(); I!=E; ++I) { + // Decrement by 1 because we are using a vector for the lookup and + // 0 is reserved for NULL. + assert(I->second > 0); + assert(I->second-1 < idcount); + unsigned idx = I->second-1; + + // Store the mapping from persistent ID to IdentifierInfo* + IIDMap[idx].II = I->first; + + // Store the reverse mapping in a hashtable. + IIOffMap.insert(&IIDMap[idx], I->second); + } + + // Write out the inverse map first. This causes the PCIDKey entries to + // record PTH file offsets for the string data. This is used to write + // the second table. + Offset StringTableOffset = IIOffMap.Emit(Out); + + // Now emit the table mapping from persistent IDs to PTH file offsets. + Offset IDOff = Out.tell(); + Emit32(idcount); // Emit the number of identifiers. + for (unsigned i = 0 ; i < idcount; ++i) Emit32(IIDMap[i].FileOffset); + + // Finally, release the inverse map. + free(IIDMap); + + return std::make_pair(IDOff, StringTableOffset); +} diff --git a/clang/tools/clang-cc/DependencyFile.cpp b/clang/tools/clang-cc/DependencyFile.cpp new file mode 100644 index 00000000000..2140afcfefb --- /dev/null +++ b/clang/tools/clang-cc/DependencyFile.cpp @@ -0,0 +1,272 @@ +//===--- DependencyFile.cpp - Generate dependency file --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code generates dependency files. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/DirectoryLookup.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <string> + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks { + std::vector<std::string> Files; + llvm::StringSet<> FilesSet; + const Preprocessor *PP; + std::ofstream OS; + const std::string &InputFile; + std::vector<std::string> Targets; + +private: + bool FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType); + void OutputDependencyFile(); + +public: + DependencyFileCallback(const Preprocessor *PP, + const std::string &InputFile, + const std::string &DepFile, + const std::vector<std::string> &Targets, + const char *&ErrStr); + ~DependencyFileCallback(); + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType); +}; +} + +static const char *DependencyFileExt = "d"; +static const char *ObjectFileExt = "o"; + +//===----------------------------------------------------------------------===// +// Dependency file options +//===----------------------------------------------------------------------===// +static llvm::cl::opt<bool> +GenerateDependencyFile("MD", + llvm::cl::desc("Generate dependency for main source file " + "(system headers included)")); + +static llvm::cl::opt<bool> +GenerateDependencyFileNoSysHeaders("MMD", + llvm::cl::desc("Generate dependency for main source file " + "(no system headers)")); + +static llvm::cl::opt<std::string> +DependencyOutputFile("MF", + llvm::cl::desc("Specify dependency output file")); + +static llvm::cl::list<std::string> +DependencyTargets("MT", + llvm::cl::desc("Specify target for dependency")); + +// FIXME: Implement feature +static llvm::cl::opt<bool> +PhonyDependencyTarget("MP", + llvm::cl::desc("Create phony target for each dependency " + "(other than main file)")); + +bool clang::CreateDependencyFileGen(Preprocessor *PP, + std::string &OutputFile, + const std::string &InputFile, + const char *&ErrStr) { + assert(!InputFile.empty() && "No file given"); + + ErrStr = NULL; + + if (!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) { + if (!DependencyOutputFile.empty() || !DependencyTargets.empty() || + PhonyDependencyTarget) + ErrStr = "Error: to generate dependencies you must specify -MD or -MMD\n"; + return false; + } + + // Handle conflicting options + if (GenerateDependencyFileNoSysHeaders) + GenerateDependencyFile = false; + + // Determine name of dependency output filename + llvm::sys::Path DepFile; + if (!DependencyOutputFile.empty()) + DepFile = DependencyOutputFile; + else if (!OutputFile.empty()) { + DepFile = OutputFile; + DepFile.eraseSuffix(); + DepFile.appendSuffix(DependencyFileExt); + } + else { + DepFile = InputFile; + DepFile.eraseSuffix(); + DepFile.appendSuffix(DependencyFileExt); + } + + std::vector<std::string> Targets(DependencyTargets); + + // Infer target name if unspecified + if (Targets.empty()) { + if (!OutputFile.empty()) { + llvm::sys::Path TargetPath(OutputFile); + TargetPath.eraseSuffix(); + TargetPath.appendSuffix(ObjectFileExt); + Targets.push_back(TargetPath.toString()); + } else { + llvm::sys::Path TargetPath(InputFile); + TargetPath.eraseSuffix(); + TargetPath.appendSuffix(ObjectFileExt); + Targets.push_back(TargetPath.toString()); + } + } + + DependencyFileCallback *PPDep = + new DependencyFileCallback(PP, InputFile, DepFile.toString(), + Targets, ErrStr); + if (ErrStr){ + delete PPDep; + return false; + } + else { + PP->setPPCallbacks(PPDep); + return true; + } +} + +/// FileMatchesDepCriteria - Determine whether the given Filename should be +/// considered as a dependency. +bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType) { + if (strcmp(InputFile.c_str(), Filename) != 0 && + strcmp("<built-in>", Filename) != 0) { + if (GenerateDependencyFileNoSysHeaders) + return FileType == SrcMgr::C_User; + else + return true; + } + + return false; +} + +void DependencyFileCallback::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType) { + if (Reason != PPCallbacks::EnterFile) + return; + + // Depedency generation really does want to go all the way to the file entry + // for a source location to find out what is depended on. We do not want + // #line markers to affect dependency generation! + SourceManager &SM = PP->getSourceManager(); + + const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(SM.getInstantiationLoc(Loc))); + if (FE == 0) return; + + const char *Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename, FileType)) + return; + + // Remove leading "./" + if (Filename[0] == '.' && Filename[1] == '/') + Filename = &Filename[2]; + + if (FilesSet.insert(Filename)) + Files.push_back(Filename); +} + +void DependencyFileCallback::OutputDependencyFile() { + // Write out the dependency targets, trying to avoid overly long + // lines when possible. We try our best to emit exactly the same + // dependency file as GCC (4.2), assuming the included files are the + // same. + const unsigned MaxColumns = 75; + unsigned Columns = 0; + + for (std::vector<std::string>::iterator + I = Targets.begin(), E = Targets.end(); I != E; ++I) { + unsigned N = I->length(); + if (Columns == 0) { + Columns += N; + OS << *I; + } else if (Columns + N + 2 > MaxColumns) { + Columns = N + 2; + OS << " \\\n " << *I; + } else { + Columns += N + 1; + OS << " " << *I; + } + } + + OS << ":"; + Columns += 1; + + // Now add each dependency in the order it was seen, but avoiding + // duplicates. + for (std::vector<std::string>::iterator I = Files.begin(), + E = Files.end(); I != E; ++I) { + // Start a new line if this would exceed the column limit. Make + // sure to leave space for a trailing " \" in case we need to + // break the line on the next iteration. + unsigned N = I->length(); + if (Columns + (N + 1) + 2 > MaxColumns) { + OS << " \\\n "; + Columns = 2; + } + OS << " " << *I; + Columns += N + 1; + } + OS << "\n"; + + // Create phony targets if requested. + if (PhonyDependencyTarget) { + // Skip the first entry, this is always the input file itself. + for (std::vector<std::string>::iterator I = Files.begin() + 1, + E = Files.end(); I != E; ++I) { + OS << "\n"; + OS << *I << ":\n"; + } + } +} + +DependencyFileCallback::DependencyFileCallback(const Preprocessor *PP, + const std::string &InputFile, + const std::string &DepFile, + const std::vector<std::string> + &Targets, + const char *&ErrStr) + : PP(PP), InputFile(InputFile), Targets(Targets) { + + OS.open(DepFile.c_str()); + if (OS.fail()) + ErrStr = "Could not open dependency output file\n"; + else + ErrStr = NULL; + + Files.push_back(InputFile); +} + +DependencyFileCallback::~DependencyFileCallback() { + if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) || + OS.fail()) + return; + + OutputDependencyFile(); + OS.close(); +} + diff --git a/clang/tools/clang-cc/DiagChecker.cpp b/clang/tools/clang-cc/DiagChecker.cpp new file mode 100644 index 00000000000..e5be10abd52 --- /dev/null +++ b/clang/tools/clang-cc/DiagChecker.cpp @@ -0,0 +1,302 @@ +//===--- DiagChecker.cpp - Diagnostic Checking Functions ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Process the input files and check that the diagnostic messages are expected. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "ASTConsumers.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Sema/ParseAST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +using namespace clang; + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +static void EmitError(Preprocessor &PP, SourceLocation Pos, const char *String){ + unsigned ID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error, String); + PP.Diag(Pos, ID); +} + + +// USING THE DIAGNOSTIC CHECKER: +// +// Indicating that a line expects an error or a warning is simple. Put a comment +// on the line that has the diagnostic, use "expected-{error,warning}" to tag +// if it's an expected error or warning, and place the expected text between {{ +// and }} markers. The full text doesn't have to be included, only enough to +// ensure that the correct diagnostic was emitted. +// +// Here's an example: +// +// int A = B; // expected-error {{use of undeclared identifier 'B'}} +// +// You can place as many diagnostics on one line as you wish. To make the code +// more readable, you can use slash-newline to separate out the diagnostics. +// +// The simple syntax above allows each specification to match exactly one error. +// You can use the extended syntax to customize this. The extended syntax is +// "expected-<type> <n> {{diag text}}", where <type> is one of "error", +// "warning" or "note", and <n> is a positive integer. This allows the +// diagnostic to appear as many times as specified. Example: +// +// void f(); // expected-note 2 {{previous declaration is here}} +// + +/// FindDiagnostics - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in a diagnostic list. +/// +static void FindDiagnostics(const char *CommentStart, unsigned CommentLen, + DiagList &ExpectedDiags, + Preprocessor &PP, SourceLocation Pos, + const char *ExpectedStr) { + const char *CommentEnd = CommentStart+CommentLen; + unsigned ExpectedStrLen = strlen(ExpectedStr); + + // Find all expected-foo diagnostics in the string and add them to + // ExpectedDiags. + while (CommentStart != CommentEnd) { + CommentStart = std::find(CommentStart, CommentEnd, 'e'); + if (unsigned(CommentEnd-CommentStart) < ExpectedStrLen) return; + + // If this isn't expected-foo, ignore it. + if (memcmp(CommentStart, ExpectedStr, ExpectedStrLen)) { + ++CommentStart; + continue; + } + + CommentStart += ExpectedStrLen; + + // Skip whitespace. + while (CommentStart != CommentEnd && + isspace(CommentStart[0])) + ++CommentStart; + + // Default, if we find the '{' now, is 1 time. + int Times = 1; + int Temp = 0; + // In extended syntax, there could be a digit now. + while (CommentStart != CommentEnd && + CommentStart[0] >= '0' && CommentStart[0] <= '9') { + Temp *= 10; + Temp += CommentStart[0] - '0'; + ++CommentStart; + } + if (Temp > 0) + Times = Temp; + + // Skip whitespace again. + while (CommentStart != CommentEnd && + isspace(CommentStart[0])) + ++CommentStart; + + // We should have a {{ now. + if (CommentEnd-CommentStart < 2 || + CommentStart[0] != '{' || CommentStart[1] != '{') { + if (std::find(CommentStart, CommentEnd, '{') != CommentEnd) + EmitError(PP, Pos, "bogus characters before '{{' in expected string"); + else + EmitError(PP, Pos, "cannot find start ('{{') of expected string"); + return; + } + CommentStart += 2; + + // Find the }}. + const char *ExpectedEnd = CommentStart; + while (1) { + ExpectedEnd = std::find(ExpectedEnd, CommentEnd, '}'); + if (CommentEnd-ExpectedEnd < 2) { + EmitError(PP, Pos, "cannot find end ('}}') of expected string"); + return; + } + + if (ExpectedEnd[1] == '}') + break; + + ++ExpectedEnd; // Skip over singular }'s + } + + std::string Msg(CommentStart, ExpectedEnd); + std::string::size_type FindPos; + while ((FindPos = Msg.find("\\n")) != std::string::npos) + Msg.replace(FindPos, 2, "\n"); + // Add is possibly multiple times. + for (int i = 0; i < Times; ++i) + ExpectedDiags.push_back(std::make_pair(Pos, Msg)); + + CommentStart = ExpectedEnd; + } +} + +/// FindExpectedDiags - Lex the main source file to find all of the +// expected errors and warnings. +static void FindExpectedDiags(Preprocessor &PP, + DiagList &ExpectedErrors, + DiagList &ExpectedWarnings, + DiagList &ExpectedNotes) { + // Create a raw lexer to pull all the comments out of the main file. We don't + // want to look in #include'd headers for expected-error strings. + FileID FID = PP.getSourceManager().getMainFileID(); + + // Create a lexer to lex all the tokens of the main file in raw mode. + Lexer RawLex(FID, PP.getSourceManager(), PP.getLangOptions()); + + // Return comments as tokens, this is how we find expected diagnostics. + RawLex.SetCommentRetentionState(true); + + Token Tok; + Tok.setKind(tok::comment); + while (Tok.isNot(tok::eof)) { + RawLex.Lex(Tok); + if (!Tok.is(tok::comment)) continue; + + std::string Comment = PP.getSpelling(Tok); + if (Comment.empty()) continue; + + + // Find all expected errors. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedErrors, PP, + Tok.getLocation(), "expected-error"); + + // Find all expected warnings. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedWarnings, PP, + Tok.getLocation(), "expected-warning"); + + // Find all expected notes. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedNotes, PP, + Tok.getLocation(), "expected-note"); + }; +} + +/// PrintProblem - This takes a diagnostic map of the delta between expected and +/// seen diagnostics. If there's anything in it, then something unexpected +/// happened. Print the map out in a nice format and return "true". If the map +/// is empty and we're not going to print things, then return "false". +/// +static bool PrintProblem(SourceManager &SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Msg) { + if (diag_begin == diag_end) return false; + + fprintf(stderr, "%s\n", Msg); + + for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) + fprintf(stderr, " Line %d: %s\n", + SourceMgr.getInstantiationLineNumber(I->first), + I->second.c_str()); + + return true; +} + +/// CompareDiagLists - Compare two diagnostic lists and return the difference +/// between them. +/// +static bool CompareDiagLists(SourceManager &SourceMgr, + const_diag_iterator d1_begin, + const_diag_iterator d1_end, + const_diag_iterator d2_begin, + const_diag_iterator d2_end, + const char *MsgLeftOnly, + const char *MsgRightOnly) { + DiagList LeftOnly; + DiagList Left(d1_begin, d1_end); + DiagList Right(d2_begin, d2_end); + + for (const_diag_iterator I = Left.begin(), E = Left.end(); I != E; ++I) { + unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(I->first); + const std::string &Diag1 = I->second; + + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first); + if (LineNo1 != LineNo2) continue; + + const std::string &Diag2 = II->second; + if (Diag2.find(Diag1) != std::string::npos || + Diag1.find(Diag2) != std::string::npos) { + break; + } + } + if (II == IE) { + // Not found. + LeftOnly.push_back(*I); + } else { + // Found. The same cannot be found twice. + Right.erase(II); + } + } + // Now all that's left in Right are those that were not matched. + + return PrintProblem(SourceMgr, LeftOnly.begin(), LeftOnly.end(), MsgLeftOnly) + | PrintProblem(SourceMgr, Right.begin(), Right.end(), MsgRightOnly); +} + +/// CheckResults - This compares the expected results to those that +/// were actually reported. It emits any discrepencies. Return "true" if there +/// were problems. Return "false" otherwise. +/// +static bool CheckResults(Preprocessor &PP, + const DiagList &ExpectedErrors, + const DiagList &ExpectedWarnings, + const DiagList &ExpectedNotes) { + const DiagnosticClient *DiagClient = PP.getDiagnostics().getClient(); + assert(DiagClient != 0 && + "DiagChecker requires a valid TextDiagnosticBuffer"); + const TextDiagnosticBuffer &Diags = + static_cast<const TextDiagnosticBuffer&>(*DiagClient); + SourceManager &SourceMgr = PP.getSourceManager(); + + // We want to capture the delta between what was expected and what was + // seen. + // + // Expected \ Seen - set expected but not seen + // Seen \ Expected - set seen but not expected + bool HadProblem = false; + + // See if there are error mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedErrors.begin(), ExpectedErrors.end(), + Diags.err_begin(), Diags.err_end(), + "Errors expected but not seen:", + "Errors seen but not expected:"); + + // See if there are warning mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedWarnings.begin(), + ExpectedWarnings.end(), + Diags.warn_begin(), Diags.warn_end(), + "Warnings expected but not seen:", + "Warnings seen but not expected:"); + + // See if there are note mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedNotes.begin(), + ExpectedNotes.end(), + Diags.note_begin(), Diags.note_end(), + "Notes expected but not seen:", + "Notes seen but not expected:"); + + return HadProblem; +} + + +/// CheckDiagnostics - Gather the expected diagnostics and check them. +bool clang::CheckDiagnostics(Preprocessor &PP) { + // Gather the set of expected diagnostics. + DiagList ExpectedErrors, ExpectedWarnings, ExpectedNotes; + FindExpectedDiags(PP, ExpectedErrors, ExpectedWarnings, ExpectedNotes); + + // Check that the expected diagnostics occurred. + return CheckResults(PP, ExpectedErrors, ExpectedWarnings, ExpectedNotes); +} diff --git a/clang/tools/clang-cc/HTMLPrint.cpp b/clang/tools/clang-cc/HTMLPrint.cpp new file mode 100644 index 00000000000..b2bb29c9eb3 --- /dev/null +++ b/clang/tools/clang-cc/HTMLPrint.cpp @@ -0,0 +1,97 @@ +//===--- HTMLPrint.cpp - Source code -> HTML pretty-printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Pretty-printing of source code to HTML. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Rewrite/HTMLRewrite.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/ASTContext.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Functional HTML pretty-printing. +//===----------------------------------------------------------------------===// + +namespace { + class HTMLPrinter : public ASTConsumer { + Rewriter R; + std::string OutFilename; + Diagnostic &Diags; + Preprocessor *PP; + PreprocessorFactory *PPF; + public: + HTMLPrinter(const std::string &OutFile, Diagnostic &D, Preprocessor *pp, + PreprocessorFactory* ppf) + : OutFilename(OutFile), Diags(D), PP(pp), PPF(ppf) {} + virtual ~HTMLPrinter(); + + void Initialize(ASTContext &context); + }; +} + +ASTConsumer* clang::CreateHTMLPrinter(const std::string &OutFile, + Diagnostic &D, Preprocessor *PP, + PreprocessorFactory* PPF) { + + return new HTMLPrinter(OutFile, D, PP, PPF); +} + +void HTMLPrinter::Initialize(ASTContext &context) { + R.setSourceMgr(context.getSourceManager()); +} + +HTMLPrinter::~HTMLPrinter() { + if (Diags.hasErrorOccurred()) + return; + + // Format the file. + FileID FID = R.getSourceMgr().getMainFileID(); + const FileEntry* Entry = R.getSourceMgr().getFileEntryForID(FID); + + html::AddLineNumbers(R, FID); + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); + + // If we have a preprocessor, relex the file and syntax highlight. + // We might not have a preprocessor if we come from a deserialized AST file, + // for example. + + if (PP) html::SyntaxHighlight(R, FID, *PP); + if (PPF) html::HighlightMacros(R, FID, *PP); + html::EscapeText(R, FID, false, true); + + // Open the output. + FILE *OutputFILE; + if (OutFilename.empty() || OutFilename == "-") + OutputFILE = stdout; + else { + OutputFILE = fopen(OutFilename.c_str(), "w+"); + if (OutputFILE == 0) { + fprintf(stderr, "Error opening output file '%s'.\n", OutFilename.c_str()); + exit(1); + } + } + + // Emit the HTML. + const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID); + char *Buffer = (char*)malloc(RewriteBuf.size()); + std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer); + fwrite(Buffer, 1, RewriteBuf.size(), OutputFILE); + free(Buffer); + + if (OutputFILE != stdout) fclose(OutputFILE); +} diff --git a/clang/tools/clang-cc/Makefile b/clang/tools/clang-cc/Makefile new file mode 100644 index 00000000000..4539665d2e8 --- /dev/null +++ b/clang/tools/clang-cc/Makefile @@ -0,0 +1,28 @@ +##===- tools/clang-cc/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. + +TOOLNAME = clang-cc +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include +CXXFLAGS = -fno-rtti + +# Clang has no plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitreader bitwriter codegen ipo selectiondag +USEDLIBS = clangCodeGen.a clangAnalysis.a clangRewrite.a clangSema.a \ + clangFrontend.a clangAST.a clangParse.a clangLex.a \ + clangBasic.a + +include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/clang/tools/clang-cc/PrintParserCallbacks.cpp b/clang/tools/clang-cc/PrintParserCallbacks.cpp new file mode 100644 index 00000000000..6230591ffcc --- /dev/null +++ b/clang/tools/clang-cc/PrintParserCallbacks.cpp @@ -0,0 +1,819 @@ +//===--- PrintParserActions.cpp - Implement -parse-print-callbacks mode ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code simply runs the preprocessor on the input file and prints out the +// result. This is the traditional behavior of the -E option. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Parse/Action.h" +#include "clang/Parse/DeclSpec.h" +#include "llvm/Support/Streams.h" +using namespace clang; + +namespace { + class ParserPrintActions : public MinimalAction { + + public: + ParserPrintActions(Preprocessor &PP) : MinimalAction(PP) {} + + // Printing Functions which also must call MinimalAction + + /// ActOnDeclarator - This callback is invoked when a declarator is parsed + /// and 'Init' specifies the initializer if any. This is for things like: + /// "int X = 4" or "typedef int foo". + virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, + DeclTy *LastInGroup) { + llvm::cout << __FUNCTION__ << " "; + if (IdentifierInfo *II = D.getIdentifier()) { + llvm::cout << "'" << II->getName() << "'"; + } else { + llvm::cout << "<anon>"; + } + llvm::cout << "\n"; + + // Pass up to EmptyActions so that the symbol table is maintained right. + return MinimalAction::ActOnDeclarator(S, D, LastInGroup); + } + /// ActOnPopScope - This callback is called immediately before the specified + /// scope is popped and deleted. + virtual void ActOnPopScope(SourceLocation Loc, Scope *S) { + llvm::cout << __FUNCTION__ << "\n"; + return MinimalAction::ActOnPopScope(Loc, S); + } + + /// ActOnTranslationUnitScope - This callback is called once, immediately + /// after creating the translation unit scope (in Parser::Initialize). + virtual void ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) { + llvm::cout << __FUNCTION__ << "\n"; + MinimalAction::ActOnTranslationUnitScope(Loc, S); + } + + + Action::DeclTy *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, + IdentifierInfo *ClassName, + SourceLocation ClassLoc, + IdentifierInfo *SuperName, + SourceLocation SuperLoc, + DeclTy * const *ProtoRefs, + unsigned NumProtocols, + SourceLocation EndProtoLoc, + AttributeList *AttrList) { + llvm::cout << __FUNCTION__ << "\n"; + return MinimalAction::ActOnStartClassInterface(AtInterfaceLoc, + ClassName, ClassLoc, + SuperName, SuperLoc, + ProtoRefs, NumProtocols, + EndProtoLoc, AttrList); + } + + /// ActOnForwardClassDeclaration - + /// Scope will always be top level file scope. + Action::DeclTy *ActOnForwardClassDeclaration(SourceLocation AtClassLoc, + IdentifierInfo **IdentList, + unsigned NumElts) { + llvm::cout << __FUNCTION__ << "\n"; + return MinimalAction::ActOnForwardClassDeclaration(AtClassLoc, IdentList, + NumElts); + } + + // Pure Printing + + /// ActOnParamDeclarator - This callback is invoked when a parameter + /// declarator is parsed. This callback only occurs for functions + /// with prototypes. S is the function prototype scope for the + /// parameters (C++ [basic.scope.proto]). + virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D) { + llvm::cout << __FUNCTION__ << " "; + if (IdentifierInfo *II = D.getIdentifier()) { + llvm::cout << "'" << II->getName() << "'"; + } else { + llvm::cout << "<anon>"; + } + llvm::cout << "\n"; + return 0; + } + + /// AddInitializerToDecl - This action is called immediately after + /// ParseDeclarator (when an initializer is present). The code is factored + /// this way to make sure we are able to handle the following: + /// void func() { int xx = xx; } + /// This allows ActOnDeclarator to register "xx" prior to parsing the + /// initializer. The declaration above should still result in a warning, + /// since the reference to "xx" is uninitialized. + virtual void AddInitializerToDecl(DeclTy *Dcl, ExprArg Init) { + llvm::cout << __FUNCTION__ << "\n"; + } + + /// FinalizeDeclaratorGroup - After a sequence of declarators are parsed, this + /// gives the actions implementation a chance to process the group as a whole. + virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// ActOnStartOfFunctionDef - This is called at the start of a function + /// definition, instead of calling ActOnDeclarator. The Declarator includes + /// information about formal arguments that are part of this function. + virtual DeclTy *ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// ActOnStartOfFunctionDef - This is called at the start of a function + /// definition, after the FunctionDecl has already been created. + virtual DeclTy *ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclTy *D) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual void ActOnStartOfObjCMethodDef(Scope *FnBodyScope, DeclTy *D) { + llvm::cout << __FUNCTION__ << "\n"; + } + + /// ActOnFunctionDefBody - This is called when a function body has completed + /// parsing. Decl is the DeclTy returned by ParseStartOfFunctionDef. + virtual DeclTy *ActOnFinishFunctionBody(DeclTy *Decl, StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual DeclTy *ActOnFileScopeAsmDecl(SourceLocation Loc, ExprArg AsmString) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with + /// no declarator (e.g. "struct foo;") is parsed. + virtual DeclTy *ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// ActOnLinkageSpec - Parsed a C++ linkage-specification that + /// contained braces. Lang/StrSize contains the language string that + /// was parsed at location Loc. Decls/NumDecls provides the + /// declarations parsed inside the linkage specification. + virtual DeclTy *ActOnLinkageSpec(SourceLocation Loc, SourceLocation LBrace, + SourceLocation RBrace, const char *Lang, + unsigned StrSize, + DeclTy **Decls, unsigned NumDecls) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// ActOnLinkageSpec - Parsed a C++ linkage-specification without + /// braces. Lang/StrSize contains the language string that was + /// parsed at location Loc. D is the declaration parsed. + virtual DeclTy *ActOnLinkageSpec(SourceLocation Loc, const char *Lang, + unsigned StrSize, DeclTy *D) { + return 0; + } + + //===--------------------------------------------------------------------===// + // Type Parsing Callbacks. + //===--------------------------------------------------------------------===// + + virtual TypeResult ActOnTypeName(Scope *S, Declarator &D) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK, + SourceLocation KWLoc, const CXXScopeSpec &SS, + IdentifierInfo *Name, SourceLocation NameLoc, + AttributeList *Attr) { + // TagType is an instance of DeclSpec::TST, indicating what kind of tag this + // is (struct/union/enum/class). + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + /// Act on @defs() element found when parsing a structure. ClassName is the + /// name of the referenced class. + virtual void ActOnDefs(Scope *S, DeclTy *TagD, SourceLocation DeclStart, + IdentifierInfo *ClassName, + llvm::SmallVectorImpl<DeclTy*> &Decls) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual DeclTy *ActOnField(Scope *S, DeclTy *TagD, + SourceLocation DeclStart, + Declarator &D, ExprTy *BitfieldWidth) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual DeclTy *ActOnIvar(Scope *S, SourceLocation DeclStart, + Declarator &D, ExprTy *BitfieldWidth, + tok::ObjCKeywordKind visibility) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual void ActOnFields(Scope* S, SourceLocation RecLoc, DeclTy *TagDecl, + DeclTy **Fields, unsigned NumFields, + SourceLocation LBrac, SourceLocation RBrac, + AttributeList *AttrList) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual DeclTy *ActOnEnumConstant(Scope *S, DeclTy *EnumDecl, + DeclTy *LastEnumConstant, + SourceLocation IdLoc, IdentifierInfo *Id, + SourceLocation EqualLoc, ExprTy *Val) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual void ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDecl, + DeclTy **Elements, unsigned NumElements) { + llvm::cout << __FUNCTION__ << "\n"; + } + + //===--------------------------------------------------------------------===// + // Statement Parsing Callbacks. + //===--------------------------------------------------------------------===// + + virtual OwningStmtResult ActOnNullStmt(SourceLocation SemiLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnCompoundStmt(SourceLocation L, + SourceLocation R, + MultiStmtArg Elts, + bool isStmtExpr) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnDeclStmt(DeclTy *Decl, + SourceLocation StartLoc, + SourceLocation EndLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnExprStmt(ExprArg Expr) { + llvm::cout << __FUNCTION__ << "\n"; + return OwningStmtResult(*this, Expr.release()); + } + + /// ActOnCaseStmt - Note that this handles the GNU 'case 1 ... 4' extension, + /// which can specify an RHS value. + virtual OwningStmtResult ActOnCaseStmt(SourceLocation CaseLoc, + ExprArg LHSVal, + SourceLocation DotDotDotLoc, + ExprArg RHSVal, + SourceLocation ColonLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnDefaultStmt(SourceLocation DefaultLoc, + SourceLocation ColonLoc, + StmtArg SubStmt, Scope *CurScope){ + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnLabelStmt(SourceLocation IdentLoc, + IdentifierInfo *II, + SourceLocation ColonLoc, + StmtArg SubStmt) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnIfStmt(SourceLocation IfLoc, ExprArg CondVal, + StmtArg ThenVal,SourceLocation ElseLoc, + StmtArg ElseVal) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnStartOfSwitchStmt(ExprArg Cond) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc, + StmtArg Switch, + StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnWhileStmt(SourceLocation WhileLoc, + ExprArg Cond, StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnDoStmt(SourceLocation DoLoc, StmtArg Body, + SourceLocation WhileLoc, ExprArg Cond){ + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnForStmt(SourceLocation ForLoc, + SourceLocation LParenLoc, + StmtArg First, ExprArg Second, + ExprArg Third, SourceLocation RParenLoc, + StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnObjCForCollectionStmt( + SourceLocation ForColLoc, + SourceLocation LParenLoc, + StmtArg First, ExprArg Second, + SourceLocation RParenLoc, StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnGotoStmt(SourceLocation GotoLoc, + SourceLocation LabelLoc, + IdentifierInfo *LabelII) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnIndirectGotoStmt(SourceLocation GotoLoc, + SourceLocation StarLoc, + ExprArg DestExp) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnContinueStmt(SourceLocation ContinueLoc, + Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnBreakStmt(SourceLocation GotoLoc, + Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnReturnStmt(SourceLocation ReturnLoc, + ExprArg RetValExp) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + virtual OwningStmtResult ActOnAsmStmt(SourceLocation AsmLoc, + bool IsSimple, + bool IsVolatile, + unsigned NumOutputs, + unsigned NumInputs, + std::string *Names, + MultiExprArg Constraints, + MultiExprArg Exprs, + ExprArg AsmString, + MultiExprArg Clobbers, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + // Objective-c statements + virtual OwningStmtResult ActOnObjCAtCatchStmt(SourceLocation AtLoc, + SourceLocation RParen, + DeclTy *Parm, StmtArg Body, + StmtArg CatchList) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnObjCAtFinallyStmt(SourceLocation AtLoc, + StmtArg Body) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnObjCAtTryStmt(SourceLocation AtLoc, + StmtArg Try, StmtArg Catch, + StmtArg Finally) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnObjCAtThrowStmt(SourceLocation AtLoc, + ExprArg Throw, + Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnObjCAtSynchronizedStmt(SourceLocation AtLoc, + ExprArg SynchExpr, + StmtArg SynchBody) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + // C++ Statements + virtual DeclTy *ActOnExceptionDeclarator(Scope *S, Declarator &D) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual OwningStmtResult ActOnCXXCatchBlock(SourceLocation CatchLoc, + DeclTy *ExceptionDecl, + StmtArg HandlerBlock) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + virtual OwningStmtResult ActOnCXXTryBlock(SourceLocation TryLoc, + StmtArg TryBlock, + MultiStmtArg Handlers) { + llvm::cout << __FUNCTION__ << "\n"; + return StmtEmpty(); + } + + //===--------------------------------------------------------------------===// + // Expression Parsing Callbacks. + //===--------------------------------------------------------------------===// + + // Primary Expressions. + + /// ActOnIdentifierExpr - Parse an identifier in expression context. + /// 'HasTrailingLParen' indicates whether or not the identifier has a '(' + /// token immediately after it. + virtual OwningExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc, + IdentifierInfo &II, + bool HasTrailingLParen, + const CXXScopeSpec *SS, + bool isAddressOfOperand) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXOperatorFunctionIdExpr( + Scope *S, SourceLocation OperatorLoc, + OverloadedOperatorKind Op, + bool HasTrailingLParen, const CXXScopeSpec &SS, + bool isAddressOfOperand) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXConversionFunctionExpr( + Scope *S, SourceLocation OperatorLoc, + TypeTy *Type, bool HasTrailingLParen, + const CXXScopeSpec &SS,bool isAddressOfOperand) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnPredefinedExpr(SourceLocation Loc, + tok::TokenKind Kind) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCharacterConstant(const Token &) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnNumericConstant(const Token &) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + /// ActOnStringLiteral - The specified tokens were lexed as pasted string + /// fragments (e.g. "foo" "bar" L"baz"). + virtual OwningExprResult ActOnStringLiteral(const Token *Toks, + unsigned NumToks) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnParenExpr(SourceLocation L, SourceLocation R, + ExprArg Val) { + llvm::cout << __FUNCTION__ << "\n"; + return move(Val); // Default impl returns operand. + } + + // Postfix Expressions. + virtual OwningExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Kind, + ExprArg Input) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + virtual OwningExprResult ActOnArraySubscriptExpr(Scope *S, ExprArg Base, + SourceLocation LLoc, + ExprArg Idx, + SourceLocation RLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + virtual OwningExprResult ActOnMemberReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation MemberLoc, + IdentifierInfo &Member, + DeclTy *ImplDecl) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCallExpr(Scope *S, ExprArg Fn, + SourceLocation LParenLoc, + MultiExprArg Args, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + // Unary Operators. 'Tok' is the token for the operator. + virtual OwningExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc, + tok::TokenKind Op, ExprArg Input) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + virtual OwningExprResult + ActOnSizeOfAlignOfExpr(SourceLocation OpLoc, bool isSizeof, bool isType, + void *TyOrEx, const SourceRange &ArgRange) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCompoundLiteral(SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen, + ExprArg Op) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + virtual OwningExprResult ActOnInitList(SourceLocation LParenLoc, + MultiExprArg InitList, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + virtual OwningExprResult ActOnCastExpr(SourceLocation LParenLoc, TypeTy *Ty, + SourceLocation RParenLoc,ExprArg Op){ + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, + ExprArg LHS, ExprArg RHS) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + /// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null + /// in the case of a the GNU conditional expr extension. + virtual OwningExprResult ActOnConditionalOp(SourceLocation QuestionLoc, + SourceLocation ColonLoc, + ExprArg Cond, ExprArg LHS, + ExprArg RHS) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + //===--------------------- GNU Extension Expressions ------------------===// + + virtual OwningExprResult ActOnAddrLabel(SourceLocation OpLoc, + SourceLocation LabLoc, + IdentifierInfo *LabelII) {// "&&foo" + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnStmtExpr(SourceLocation LPLoc, + StmtArg SubStmt, + SourceLocation RPLoc) { // "({..})" + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnBuiltinOffsetOf(Scope *S, + SourceLocation BuiltinLoc, + SourceLocation TypeLoc, + TypeTy *Arg1, + OffsetOfComponent *CompPtr, + unsigned NumComponents, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + // __builtin_types_compatible_p(type1, type2) + virtual OwningExprResult ActOnTypesCompatibleExpr(SourceLocation BuiltinLoc, + TypeTy *arg1,TypeTy *arg2, + SourceLocation RPLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + // __builtin_choose_expr(constExpr, expr1, expr2) + virtual OwningExprResult ActOnChooseExpr(SourceLocation BuiltinLoc, + ExprArg cond, ExprArg expr1, + ExprArg expr2, + SourceLocation RPLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + // __builtin_va_arg(expr, type) + virtual OwningExprResult ActOnVAArg(SourceLocation BuiltinLoc, + ExprArg expr, TypeTy *type, + SourceLocation RPLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnGNUNullExpr(SourceLocation TokenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual void ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnBlockArguments(Declarator &ParamInfo, Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual OwningExprResult ActOnBlockStmtExpr(SourceLocation CaretLoc, + StmtArg Body, + Scope *CurScope) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual DeclTy *ActOnStartNamespaceDef(Scope *S, SourceLocation IdentLoc, + IdentifierInfo *Ident, + SourceLocation LBrace) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual void ActOnFinishNamespaceDef(DeclTy *Dcl,SourceLocation RBrace) { + llvm::cout << __FUNCTION__ << "\n"; + return; + } + +#if 0 + // FIXME: AttrList should be deleted by this function, but the definition + // would have to be available. + virtual DeclTy *ActOnUsingDirective(Scope *CurScope, + SourceLocation UsingLoc, + SourceLocation NamespcLoc, + const CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *NamespcName, + AttributeList *AttrList) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } +#endif + + virtual void ActOnParamDefaultArgument(DeclTy *param, + SourceLocation EqualLoc, + ExprArg defarg) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnParamUnparsedDefaultArgument(DeclTy *param, + SourceLocation EqualLoc) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnParamDefaultArgumentError(DeclTy *param) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void AddCXXDirectInitializerToDecl(DeclTy *Dcl, + SourceLocation LParenLoc, + MultiExprArg Exprs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return; + } + + virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method) + { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *Param) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, + DeclTy *Method) { + llvm::cout << __FUNCTION__ << "\n"; + } + + virtual DeclTy *ActOnStaticAssertDeclaration(SourceLocation AssertLoc, + ExprArg AssertExpr, + ExprArg AssertMessageExpr) { + llvm::cout << __FUNCTION__ << "\n"; + return 0; + } + + virtual OwningExprResult ActOnCXXNamedCast(SourceLocation OpLoc, + tok::TokenKind Kind, + SourceLocation LAngleBracketLoc, + TypeTy *Ty, + SourceLocation RAngleBracketLoc, + SourceLocation LParenLoc, + ExprArg Op, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXTypeid(SourceLocation OpLoc, + SourceLocation LParenLoc, + bool isType, void *TyOrExpr, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXThis(SourceLocation ThisLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, + tok::TokenKind Kind) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXThrow(SourceLocation OpLoc, ExprArg Op) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXTypeConstructExpr(SourceRange TypeRange, + TypeTy *TypeRep, + SourceLocation LParenLoc, + MultiExprArg Exprs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXConditionDeclarationExpr(Scope *S, + SourceLocation StartLoc, + Declarator &D, + SourceLocation EqualLoc, + ExprArg AssignExprVal) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXNew(SourceLocation StartLoc, + bool UseGlobal, + SourceLocation PlacementLParen, + MultiExprArg PlacementArgs, + SourceLocation PlacementRParen, + bool ParenTypeId, Declarator &D, + SourceLocation ConstructorLParen, + MultiExprArg ConstructorArgs, + SourceLocation ConstructorRParen) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnCXXDelete(SourceLocation StartLoc, + bool UseGlobal, bool ArrayForm, + ExprArg Operand) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + + virtual OwningExprResult ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen) { + llvm::cout << __FUNCTION__ << "\n"; + return ExprEmpty(); + } + }; +} + +MinimalAction *clang::CreatePrintParserActionsAction(Preprocessor &PP) { + return new ParserPrintActions(PP); +} diff --git a/clang/tools/clang-cc/PrintPreprocessedOutput.cpp b/clang/tools/clang-cc/PrintPreprocessedOutput.cpp new file mode 100644 index 00000000000..0e95c914c4a --- /dev/null +++ b/clang/tools/clang-cc/PrintPreprocessedOutput.cpp @@ -0,0 +1,469 @@ +//===--- PrintPreprocessedOutput.cpp - Implement the -E mode --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code simply runs the preprocessor on the input file and prints out the +// result. This is the traditional behavior of the -E option. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/SourceManager.h" +#include "clang.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/TokenConcatenation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Config/config.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Preprocessed token printer +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +DisableLineMarkers("P", llvm::cl::desc("Disable linemarker output in -E mode")); +static llvm::cl::opt<bool> +EnableCommentOutput("C", llvm::cl::desc("Enable comment output in -E mode")); +static llvm::cl::opt<bool> +EnableMacroCommentOutput("CC", + llvm::cl::desc("Enable comment output in -E mode, " + "even from macro expansions")); +static llvm::cl::opt<bool> +DumpMacros("dM", llvm::cl::desc("Print macro definitions in -E mode instead of" + " normal output")); + + +namespace { +class PrintPPOutputPPCallbacks : public PPCallbacks { + Preprocessor &PP; + TokenConcatenation ConcatInfo; +public: + llvm::raw_ostream &OS; +private: + unsigned CurLine; + bool EmittedTokensOnThisLine; + SrcMgr::CharacteristicKind FileType; + llvm::SmallString<512> CurFilename; + bool Initialized; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, llvm::raw_ostream &os) + : PP(pp), ConcatInfo(PP), OS(os) { + CurLine = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + } + + void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType); + virtual void Ident(SourceLocation Loc, const std::string &str); + virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, + const std::string &Str); + + + bool HandleFirstTokOnLine(Token &Tok); + bool MoveToLine(SourceLocation Loc); + bool AvoidConcat(const Token &PrevTok, const Token &Tok) { + return ConcatInfo.AvoidConcat(PrevTok, Tok); + } + void WriteLineInfo(unsigned LineNo, const char *Extra=0, unsigned ExtraLen=0); +}; +} // end anonymous namespace + +void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo, + const char *Extra, + unsigned ExtraLen) { + if (EmittedTokensOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + } + + OS << '#' << ' ' << LineNo << ' ' << '"'; + OS.write(&CurFilename[0], CurFilename.size()); + OS << '"'; + + if (ExtraLen) + OS.write(Extra, ExtraLen); + + if (FileType == SrcMgr::C_System) + OS.write(" 3", 2); + else if (FileType == SrcMgr::C_ExternCSystem) + OS.write(" 3 4", 4); + OS << '\n'; +} + +/// MoveToLine - Move the output to the source line specified by the location +/// object. We can do this by emitting some number of \n's, or be emitting a +/// #line directive. This returns false if already at the specified line, true +/// if some newlines were emitted. +bool PrintPPOutputPPCallbacks::MoveToLine(SourceLocation Loc) { + unsigned LineNo = PP.getSourceManager().getInstantiationLineNumber(Loc); + + if (DisableLineMarkers) { + if (LineNo == CurLine) return false; + + CurLine = LineNo; + + if (!EmittedTokensOnThisLine) + return true; + + OS << '\n'; + EmittedTokensOnThisLine = false; + return true; + } + + // If this line is "close enough" to the original line, just print newlines, + // otherwise print a #line directive. + if (LineNo-CurLine <= 8) { + if (LineNo-CurLine == 1) + OS << '\n'; + else if (LineNo == CurLine) + return false; // Spelling line moved, but instantiation line didn't. + else { + const char *NewLines = "\n\n\n\n\n\n\n\n"; + OS.write(NewLines, LineNo-CurLine); + } + } else { + WriteLineInfo(LineNo, 0, 0); + } + + CurLine = LineNo; + return true; +} + + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. Update our conception of the current source +/// position. +void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType) { + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + SourceManager &SourceMgr = PP.getSourceManager(); + if (Reason == PPCallbacks::EnterFile) { + SourceLocation IncludeLoc = SourceMgr.getPresumedLoc(Loc).getIncludeLoc(); + if (IncludeLoc.isValid()) + MoveToLine(IncludeLoc); + } else if (Reason == PPCallbacks::SystemHeaderPragma) { + MoveToLine(Loc); + + // TODO GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. Emulate this + // strange behavior. + } + + Loc = SourceMgr.getInstantiationLoc(Loc); + CurLine = SourceMgr.getInstantiationLineNumber(Loc); + + if (DisableLineMarkers) return; + + CurFilename.clear(); + CurFilename += SourceMgr.getPresumedLoc(Loc).getFilename(); + Lexer::Stringify(CurFilename); + FileType = NewFileType; + + if (!Initialized) { + WriteLineInfo(CurLine); + Initialized = true; + } + + switch (Reason) { + case PPCallbacks::EnterFile: + WriteLineInfo(CurLine, " 1", 2); + break; + case PPCallbacks::ExitFile: + WriteLineInfo(CurLine, " 2", 2); + break; + case PPCallbacks::SystemHeaderPragma: + case PPCallbacks::RenameFile: + WriteLineInfo(CurLine); + break; + } +} + +/// Ident - Handle #ident directives when read by the preprocessor. +/// +void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { + MoveToLine(Loc); + + OS.write("#ident ", strlen("#ident ")); + OS.write(&S[0], S.size()); + EmittedTokensOnThisLine = true; +} + +void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, + const IdentifierInfo *Kind, + const std::string &Str) { + MoveToLine(Loc); + OS << "#pragma comment(" << Kind->getName(); + + if (!Str.empty()) { + OS << ", \""; + + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char Char = Str[i]; + if (isprint(Char) && Char != '\\' && Char != '"') + OS << (char)Char; + else // Output anything hard as an octal escape. + OS << '\\' + << (char)('0'+ ((Char >> 6) & 7)) + << (char)('0'+ ((Char >> 3) & 7)) + << (char)('0'+ ((Char >> 0) & 7)); + } + OS << '"'; + } + + OS << ')'; + EmittedTokensOnThisLine = true; +} + + +/// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this +/// is called for the first token on each new line. If this really is the start +/// of a new logical line, handle it and return true, otherwise return false. +/// This may not be the start of a logical line because the "start of line" +/// marker is set for spelling lines, not instantiation ones. +bool PrintPPOutputPPCallbacks::HandleFirstTokOnLine(Token &Tok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + if (!MoveToLine(Tok.getLocation())) + return false; + + // Print out space characters so that the first token on a line is + // indented for easy reading. + const SourceManager &SourceMgr = PP.getSourceManager(); + unsigned ColNo = SourceMgr.getInstantiationColumnNumber(Tok.getLocation()); + + // This hack prevents stuff like: + // #define HASH # + // HASH define foo bar + // From having the # character end up at column 1, which makes it so it + // is not handled as a #define next time through the preprocessor if in + // -fpreprocessed mode. + if (ColNo <= 1 && Tok.is(tok::hash)) + OS << ' '; + + // Otherwise, indent the appropriate number of spaces. + for (; ColNo > 1; --ColNo) + OS << ' '; + + return true; +} + +namespace { +struct UnknownPragmaHandler : public PragmaHandler { + const char *Prefix; + PrintPPOutputPPCallbacks *Callbacks; + + UnknownPragmaHandler(const char *prefix, PrintPPOutputPPCallbacks *callbacks) + : PragmaHandler(0), Prefix(prefix), Callbacks(callbacks) {} + virtual void HandlePragma(Preprocessor &PP, Token &PragmaTok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->MoveToLine(PragmaTok.getLocation()); + Callbacks->OS.write(Prefix, strlen(Prefix)); + + // Read and print all of the pragma tokens. + while (PragmaTok.isNot(tok::eom)) { + if (PragmaTok.hasLeadingSpace()) + Callbacks->OS << ' '; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + PP.LexUnexpandedToken(PragmaTok); + } + Callbacks->OS << '\n'; + } +}; +} // end anonymous namespace + + +static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, + PrintPPOutputPPCallbacks *Callbacks, + llvm::raw_ostream &OS) { + char Buffer[256]; + Token PrevTok; + while (1) { + + // If this token is at the start of a line, emit newlines if needed. + if (Tok.isAtStartOfLine() && Callbacks->HandleFirstTokOnLine(Tok)) { + // done. + } else if (Tok.hasLeadingSpace() || + // If we haven't emitted a token on this line yet, PrevTok isn't + // useful to look at and no concatenation could happen anyway. + (Callbacks->hasEmittedTokensOnThisLine() && + // Don't print "-" next to "-", it would form "--". + Callbacks->AvoidConcat(PrevTok, Tok))) { + OS << ' '; + } + + if (IdentifierInfo *II = Tok.getIdentifierInfo()) { + OS.write(II->getName(), II->getLength()); + } else if (Tok.isLiteral() && !Tok.needsCleaning() && + Tok.getLiteralData()) { + OS.write(Tok.getLiteralData(), Tok.getLength()); + } else if (Tok.getLength() < 256) { + const char *TokPtr = Buffer; + unsigned Len = PP.getSpelling(Tok, TokPtr); + OS.write(TokPtr, Len); + } else { + std::string S = PP.getSpelling(Tok); + OS.write(&S[0], S.size()); + } + Callbacks->SetEmittedTokensOnThisLine(); + + if (Tok.is(tok::eof)) break; + + PrevTok = Tok; + PP.Lex(Tok); + } +} + +/// PrintMacroDefinition - Print a macro definition in a form that will be +/// properly accepted back as a definition. +static void PrintMacroDefinition(IdentifierInfo &II, const MacroInfo &MI, + Preprocessor &PP, llvm::raw_ostream &OS) { + // Ignore computed macros like __LINE__ and friends. + if (MI.isBuiltinMacro()) return; + OS << "#define " << II.getName(); + + if (MI.isFunctionLike()) { + OS << '('; + if (MI.arg_empty()) + ; + else if (MI.getNumArgs() == 1) + OS << (*MI.arg_begin())->getName(); + else { + MacroInfo::arg_iterator AI = MI.arg_begin(), E = MI.arg_end(); + OS << (*AI++)->getName(); + while (AI != E) + OS << ',' << (*AI++)->getName(); + } + + if (MI.isVariadic()) { + if (!MI.arg_empty()) + OS << ','; + OS << "..."; + } + OS << ')'; + } + + // GCC always emits a space, even if the macro body is empty. However, do not + // want to emit two spaces if the first token has a leading space. + if (MI.tokens_empty() || !MI.tokens_begin()->hasLeadingSpace()) + OS << ' '; + + llvm::SmallVector<char, 128> SpellingBuffer; + for (MacroInfo::tokens_iterator I = MI.tokens_begin(), E = MI.tokens_end(); + I != E; ++I) { + if (I->hasLeadingSpace()) + OS << ' '; + + // Make sure we have enough space in the spelling buffer. + if (I->getLength() < SpellingBuffer.size()) + SpellingBuffer.resize(I->getLength()); + const char *Buffer = &SpellingBuffer[0]; + unsigned SpellingLen = PP.getSpelling(*I, Buffer); + OS.write(Buffer, SpellingLen); + } + OS << "\n"; +} + +namespace { + struct SortMacrosByID { + typedef std::pair<IdentifierInfo*, MacroInfo*> id_macro_pair; + bool operator()(const id_macro_pair &LHS, const id_macro_pair &RHS) const { + return strcmp(LHS.first->getName(), RHS.first->getName()) < 0; + } + }; +} + +/// DoPrintPreprocessedInput - This implements -E mode. +/// +void clang::DoPrintPreprocessedInput(Preprocessor &PP, + const std::string &OutFile) { + // Inform the preprocessor whether we want it to retain comments or not, due + // to -C or -CC. + PP.SetCommentRetentionState(EnableCommentOutput, EnableMacroCommentOutput); + + // Open the output buffer. + std::string Err; + llvm::raw_fd_ostream OS(OutFile.empty() ? "-" : OutFile.c_str(), false, Err); + if (!Err.empty()) { + fprintf(stderr, "%s\n", Err.c_str()); + exit(1); + } + + OS.SetBufferSize(64*1024); + + if (DumpMacros) { + // -dM mode just scans and ignores all tokens in the files, then dumps out + // the macro table at the end. + PP.EnterMainSourceFile(); + + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof)); + + std::vector<std::pair<IdentifierInfo*, MacroInfo*> > MacrosByID; + for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); + I != E; ++I) + MacrosByID.push_back(*I); + std::sort(MacrosByID.begin(), MacrosByID.end(), SortMacrosByID()); + + for (unsigned i = 0, e = MacrosByID.size(); i != e; ++i) + PrintMacroDefinition(*MacrosByID[i].first, *MacrosByID[i].second, PP, OS); + + } else { + PrintPPOutputPPCallbacks *Callbacks + = new PrintPPOutputPPCallbacks(PP, OS); + PP.AddPragmaHandler(0, new UnknownPragmaHandler("#pragma", Callbacks)); + PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC", + Callbacks)); + + PP.setPPCallbacks(Callbacks); + + // After we have configured the preprocessor, enter the main file. + PP.EnterMainSourceFile(); + + // Consume all of the tokens that come from the predefines buffer. Those + // should not be emitted into the output and are guaranteed to be at the + // start. + const SourceManager &SourceMgr = PP.getSourceManager(); + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() && + !strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(), + "<built-in>")); + + // Read all the preprocessed tokens, printing them out to the stream. + PrintPreprocessedTokens(PP, Tok, Callbacks, OS); + OS << '\n'; + } + + // Flush the ostream. + OS.flush(); + + // If an error occurred, remove the output file. + if (PP.getDiagnostics().hasErrorOccurred() && !OutFile.empty()) + llvm::sys::Path(OutFile).eraseFromDisk(); +} + diff --git a/clang/tools/clang-cc/RewriteBlocks.cpp b/clang/tools/clang-cc/RewriteBlocks.cpp new file mode 100644 index 00000000000..2672d32ef05 --- /dev/null +++ b/clang/tools/clang-cc/RewriteBlocks.cpp @@ -0,0 +1,1146 @@ +//===--- RewriteBlocks.cpp ----------------------------------------------===// +// +// 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 closure rewriter. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <sstream> + +using namespace clang; +using llvm::utostr; + +namespace { + +class RewriteBlocks : public ASTConsumer { + Rewriter Rewrite; + Diagnostic &Diags; + const LangOptions &LangOpts; + unsigned RewriteFailedDiag; + + ASTContext *Context; + SourceManager *SM; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + + // 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; + + // The function/method we are rewriting. + FunctionDecl *CurFunctionDef; + ObjCMethodDecl *CurMethodDef; + + bool IsHeader; + std::string InFileName; + std::string OutFileName; + + std::string Preamble; +public: + RewriteBlocks(std::string inFile, std::string outFile, Diagnostic &D, + const LangOptions &LOpts); + ~RewriteBlocks() { + // Get the buffer corresponding to MainFileID. + // If we haven't changed it, then we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + std::string S(RewriteBuf->begin(), RewriteBuf->end()); + printf("%s\n", S.c_str()); + } else { + printf("No changes\n"); + } + } + + void Initialize(ASTContext &context); + + void InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen); + void ReplaceText(SourceLocation Start, unsigned OrigLength, + const char *NewStr, unsigned NewLength); + + // Top Level Driver code. + virtual void HandleTopLevelDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + + // Top level + Stmt *RewriteFunctionBody(Stmt *S); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + // Block specific rewrite rules. + std::string SynthesizeBlockInitExpr(BlockExpr *Exp, VarDecl *VD=0); + + void RewriteBlockCall(CallExpr *Exp); + void RewriteBlockPointerDecl(NamedDecl *VD); + void 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); + std::string 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 isBlockPointerType(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; + } + // ObjC rewrite methods. + void RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl); + void RewriteCategoryDecl(ObjCCategoryDecl *CatDecl); + void RewriteProtocolDecl(ObjCProtocolDecl *PDecl); + void RewriteMethodDecl(ObjCMethodDecl *MDecl); + + void RewriteFunctionProtoType(QualType funcType, NamedDecl *D); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void RewriteCastExpr(CastExpr *CE); + + bool PointerTypeTakesAnyBlockArguments(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, const char *&RParen); +}; + +} + +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"; +} + +RewriteBlocks::RewriteBlocks(std::string inFile, std::string outFile, + Diagnostic &D, const LangOptions &LOpts) : + Diags(D), LangOpts(LOpts) { + IsHeader = IsHeaderFile(inFile); + InFileName = inFile; + OutFileName = outFile; + CurFunctionDef = 0; + CurMethodDef = 0; + RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, + "rewriting failed"); +} + +ASTConsumer *clang::CreateBlockRewriter(const std::string& InFile, + const std::string& OutFile, + Diagnostic &Diags, + const LangOptions &LangOpts) { + return new RewriteBlocks(InFile, OutFile, Diags, LangOpts); +} + +void RewriteBlocks::Initialize(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + + // 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()); + + if (IsHeader) + Preamble = "#pragma once\n"; + 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 += "enum {\n"; + Preamble += " BLOCK_HAS_COPY_DISPOSE = (1<<25),\n"; + Preamble += " BLOCK_IS_GLOBAL = (1<<28)\n"; + Preamble += "};\n"; + if (LangOpts.Microsoft) + Preamble += "#define __OBJC_RW_EXTERN extern \"C\" __declspec(dllimport)\n"; + else + Preamble += "#define __OBJC_RW_EXTERN extern\n"; + Preamble += "// Runtime copy/destroy helper functions\n"; + Preamble += "__OBJC_RW_EXTERN void _Block_copy_assign(void *, void *);\n"; + Preamble += "__OBJC_RW_EXTERN void _Block_byref_assign_copy(void *, void *);\n"; + Preamble += "__OBJC_RW_EXTERN void _Block_destroy(void *);\n"; + Preamble += "__OBJC_RW_EXTERN void _Block_byref_release(void *);\n"; + Preamble += "__OBJC_RW_EXTERN void *_NSConcreteGlobalBlock;\n"; + Preamble += "__OBJC_RW_EXTERN void *_NSConcreteStackBlock;\n"; + Preamble += "#endif\n"; + + InsertText(SM->getLocForStartOfFile(MainFileID), + Preamble.c_str(), Preamble.size()); +} + +void RewriteBlocks::InsertText(SourceLocation Loc, const char *StrData, + unsigned StrLen) +{ + if (!Rewrite.InsertText(Loc, StrData, StrLen)) + return; + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); +} + +void RewriteBlocks::ReplaceText(SourceLocation Start, unsigned OrigLength, + const char *NewStr, unsigned NewLength) { + if (!Rewrite.ReplaceText(Start, OrigLength, NewStr, NewLength)) + return; + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); +} + +void RewriteBlocks::RewriteMethodDecl(ObjCMethodDecl *Method) { + bool haveBlockPtrs = false; + for (ObjCMethodDecl::param_iterator I = Method->param_begin(), + E = Method->param_end(); I != E; ++I) + if (isBlockPointerType((*I)->getType())) + haveBlockPtrs = true; + + if (!haveBlockPtrs) + return; + + // Do a fuzzy rewrite. + // We have 1 or more arguments that have closure pointers. + SourceLocation Loc = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(LocEnd); + + const char *methodPtr = startBuf; + std::string Tag = "struct __block_impl *"; + + while (*methodPtr++ && (methodPtr != endBuf)) { + switch (*methodPtr) { + case ':': + methodPtr++; + if (*methodPtr == '(') { + const char *scanType = ++methodPtr; + bool foundBlockPointer = false; + unsigned parenCount = 1; + + while (parenCount) { + switch (*scanType) { + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + case '^': + foundBlockPointer = true; + break; + } + scanType++; + } + if (foundBlockPointer) { + // advance the location to startArgList. + Loc = Loc.getFileLocWithOffset(methodPtr-startBuf); + assert((Loc.isValid()) && "Invalid Loc"); + ReplaceText(Loc, scanType-methodPtr-1, Tag.c_str(), Tag.size()); + + // Advance startBuf. Since the underlying buffer has changed, + // it's very important to advance startBuf (so we can correctly + // compute a relative Loc the next time around). + startBuf = methodPtr; + } + // Advance the method ptr to the end of the type. + methodPtr = scanType; + } + break; + } + } + return; +} + +void RewriteBlocks::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + for (ObjCInterfaceDecl::instmeth_iterator I = ClassDecl->instmeth_begin(), + E = ClassDecl->instmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); + for (ObjCInterfaceDecl::classmeth_iterator I = ClassDecl->classmeth_begin(), + E = ClassDecl->classmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); +} + +void RewriteBlocks::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + for (ObjCCategoryDecl::instmeth_iterator I = CatDecl->instmeth_begin(), + E = CatDecl->instmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); + for (ObjCCategoryDecl::classmeth_iterator I = CatDecl->classmeth_begin(), + E = CatDecl->classmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); +} + +void RewriteBlocks::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + for (ObjCProtocolDecl::instmeth_iterator I = PDecl->instmeth_begin(), + E = PDecl->instmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); + for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(), + E = PDecl->classmeth_end(); I != E; ++I) + RewriteMethodDecl(*I); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteBlocks::HandleTopLevelDecl(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; + + 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); + + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isFromMainFile(Loc)) + HandleDeclInMainFile(D); + return; +} + +std::string RewriteBlocks::SynthesizeBlockFunc(BlockExpr *CE, int i, + const char *funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getResultType(); + std::string StructRef = "struct " + Tag; + std::string S = "static " + RT.getAsString() + " __" + + funcName + "_" + "block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa<FunctionNoProtoType>(AFT)) { + S += "()"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast<FunctionProtoType>(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + (*AI)->getType().getAsStringInternal(ParamStr); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + Context->getPointerType((*I)->getType()).getAsStringInternal(Name); + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isBlockPointerType((*I)->getType())) + S += "struct __block_impl *"; + else + (*I)->getType().getAsStringInternal(Name); + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteBlocks::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + const char *funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + S += "_Block_copy_assign(&dst->"; + S += (*I)->getNameAsString(); + S += ", src->"; + S += (*I)->getNameAsString(); + S += ");}"; + } + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + S += "_Block_destroy(src->"; + S += (*I)->getNameAsString(); + S += ");"; + } + S += "}\n"; + return S; +} + +std::string RewriteBlocks::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + bool hasCopyDisposeHelpers) { + std::string S = "struct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + + if (hasCopyDisposeHelpers) + S += " void *copy;\n void *dispose;\n"; + + Constructor += "(void *fp"; + + if (hasCopyDisposeHelpers) + Constructor += ", void *copyHelp, void *disposeHelp"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + (*I)->getType().getAsStringInternal(FieldName); + (*I)->getType().getAsStringInternal(ArgName); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + Context->getPointerType((*I)->getType()).getAsStringInternal(FieldName); + Context->getPointerType((*I)->getType()).getAsStringInternal(ArgName); + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + // FIXME: handle NSConcreteGlobalBlock. + Constructor += ", int flags=0) {\n"; + Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; + Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + if (hasCopyDisposeHelpers) + Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; + + // Initialize all "by copy" arguments. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + Constructor += " "; + if (isBlockPointerType((*I)->getType())) + Constructor += Name + " = (struct __block_impl *)_"; + else + Constructor += Name + " = _"; + Constructor += Name + ";\n"; + } + // Initialize all "by ref" arguments. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + Constructor += " "; + if (isBlockPointerType((*I)->getType())) + Constructor += Name + " = (struct __block_impl *)_"; + else + Constructor += Name + " = _"; + Constructor += Name + ";\n"; + } + } else { + // Finish writing the constructor. + // FIXME: handle NSConcreteGlobalBlock. + Constructor += ", int flags=0) {\n"; + Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; + Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + if (hasCopyDisposeHelpers) + Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +void RewriteBlocks::SynthesizeBlockLiterals(SourceLocation FunLocStart, + const char *FunName) { + // Insert closures that were part of the function. + for (unsigned i = 0; i < Blocks.size(); i++) { + + CollectBlockDeclRefInfo(Blocks[i]); + + std::string Tag = "__" + std::string(FunName) + "_block_impl_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], Tag, + ImportedBlockDecls.size() > 0); + + InsertText(FunLocStart, CI.c_str(), CI.size()); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, Tag); + + InsertText(FunLocStart, CF.c_str(), CF.size()); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, Tag); + InsertText(FunLocStart, HF.c_str(), HF.size()); + } + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByCopyDecls.clear(); + BlockCallExprs.clear(); + ImportedBlockDecls.clear(); + } + Blocks.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteBlocks::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const char *FuncName = FD->getNameAsCString(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteBlocks::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + SourceLocation FunLocStart = MD->getLocStart(); + std::string FuncName = MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = FuncName.find(":", loc)) != std::string::npos) + FuncName.replace(loc, 1, "_"); + + SynthesizeBlockLiterals(FunLocStart, FuncName.c_str()); +} + +void RewriteBlocks::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(S)) + // FIXME: Handle enums. + if (!isa<FunctionDecl>(CDRE->getDecl())) + BlockDeclRefs.push_back(CDRE); + return; +} + +void RewriteBlocks::GetBlockCallExprs(Stmt *S) { + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockCallExprs(CBE->getBody()); + else + GetBlockCallExprs(*CI); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + BlockCallExprs[dyn_cast<BlockDeclRefExpr>(CE->getCallee())] = CE; + } + } + return; +} + +std::string RewriteBlocks::SynthesizeBlockCall(CallExpr *Exp) { + // Navigate to relevant type information. + const char *closureName = 0; + const BlockPointerType *CPT = 0; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp->getCallee())) { + closureName = DRE->getDecl()->getNameAsCString(); + CPT = DRE->getType()->getAsBlockPointerType(); + } else if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(Exp->getCallee())) { + closureName = CDRE->getDecl()->getNameAsCString(); + CPT = CDRE->getType()->getAsBlockPointerType(); + } else if (MemberExpr *MExpr = dyn_cast<MemberExpr>(Exp->getCallee())) { + closureName = MExpr->getMemberDecl()->getNameAsCString(); + CPT = MExpr->getType()->getAsBlockPointerType(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAsFunctionType(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + + // Build a closure call - start with a paren expr to enforce precedence. + std::string BlockCall = "("; + + // Synthesize the cast. + BlockCall += "(" + Exp->getType().getAsString() + "(*)"; + BlockCall += "(struct __block_impl *"; + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) + BlockCall += ", " + (*I).getAsString(); + } + BlockCall += "))"; // close the argument list and paren expression. + + // Invoke the closure. We need to cast it since the declaration type is + // bogus (it's a function pointer type) + BlockCall += "((struct __block_impl *)"; + std::string closureExprBufStr; + llvm::raw_string_ostream closureExprBuf(closureExprBufStr); + Exp->getCallee()->printPretty(closureExprBuf); + BlockCall += closureExprBuf.str(); + BlockCall += ")->FuncPtr)"; + + // Add the arguments. + BlockCall += "((struct __block_impl *)"; + BlockCall += closureExprBuf.str(); + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + std::string syncExprBufS; + llvm::raw_string_ostream Buf(syncExprBufS); + (*I)->printPretty(Buf); + BlockCall += ", " + Buf.str(); + } + return BlockCall; +} + +void RewriteBlocks::RewriteBlockCall(CallExpr *Exp) { + std::string BlockCall = SynthesizeBlockCall(Exp); + + const char *startBuf = SM->getCharacterData(Exp->getLocStart()); + const char *endBuf = SM->getCharacterData(Exp->getLocEnd()); + + ReplaceText(Exp->getLocStart(), endBuf-startBuf, + BlockCall.c_str(), BlockCall.size()); +} + +void RewriteBlocks::RewriteBlockDeclRefExpr(BlockDeclRefExpr *BDRE) { + // FIXME: Add more elaborate code generation required by the ABI. + InsertText(BDRE->getLocStart(), "*", 1); +} + +void RewriteBlocks::RewriteCastExpr(CastExpr *CE) { + SourceLocation LocStart = CE->getLocStart(); + SourceLocation LocEnd = CE->getLocEnd(); + + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getFileLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*", 1); + break; + } + } + return; +} + +void RewriteBlocks::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getFileLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getFileLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*", 1); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteBlocks::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAsPointerType(); + if (PT) { + FTP = PT->getPointeeType()->getAsFunctionProtoType(); + } else { + const BlockPointerType *BPT = QT->getAsBlockPointerType(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAsFunctionProtoType(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) + if (isBlockPointerType(*I)) + return true; + } + return false; +} + +void RewriteBlocks::GetExtentOfArgList(const char *Name, + const char *&LParen, const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteBlocks::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast<VarDecl>(ND)) + DeclT = VD->getType(); + else if (TypedefDecl *TDD = dyn_cast<TypedefDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + assert(0 && "RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + DeclLoc = DeclLoc.getFileLocWithOffset(startBuf-endBuf); + ReplaceText(DeclLoc, 1, "*", 1); + } + if (PointerTypeTakesAnyBlockArguments(DeclT)) { + // Replace the '^' with '*' for arguments. + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') { + SourceLocation CaretLoc = DeclLoc.getFileLocWithOffset(argListBegin-startBuf); + ReplaceText(CaretLoc, 1, "*", 1); + } + argListBegin++; + } + } + return; +} + +void RewriteBlocks::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->isByRef()) + BlockByCopyDecls.insert(BlockDeclRefs[i]->getDecl()); + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->isByRef()) { + BlockByRefDecls.insert(BlockDeclRefs[i]->getDecl()); + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (isBlockPointerType(BlockDeclRefs[i]->getType())) { + GetBlockCallExprs(Blocks[i]); + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } + } +} + +std::string RewriteBlocks::SynthesizeBlockInitExpr(BlockExpr *Exp, VarDecl *VD) { + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + std::string FuncName; + + if (CurFunctionDef) + FuncName = std::string(CurFunctionDef->getNameAsString()); + else if (CurMethodDef) { + FuncName = CurMethodDef->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = FuncName.find(":", loc)) != std::string::npos) + FuncName.replace(loc, 1, "_"); + } else if (VD) + FuncName = std::string(VD->getNameAsString()); + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + std::string FunkTypeStr; + + // Get a pointer to the function type so we can cast appropriately. + Context->getPointerType(QualType(Exp->getFunctionType(),0)).getAsStringInternal(FunkTypeStr); + + // Rewrite the closure block with a compound literal. The first cast is + // to prevent warnings from the C compiler. + std::string Init = "(" + FunkTypeStr; + + Init += ")&" + Tag; + + // Initialize the block function. + Init += "((void*)" + Func; + + if (ImportedBlockDecls.size()) { + std::string Buf = "__" + FuncName + "_block_copy_" + BlockNumber; + Init += ",(void*)" + Buf; + Buf = "__" + FuncName + "_block_dispose_" + BlockNumber; + Init += ",(void*)" + Buf; + } + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + Init += ","; + if (isObjCType((*I)->getType())) { + Init += "[["; + Init += (*I)->getNameAsString(); + Init += " retain] autorelease]"; + } else if (isBlockPointerType((*I)->getType())) { + Init += "(void *)"; + Init += (*I)->getNameAsString(); + } else { + Init += (*I)->getNameAsString(); + } + } + // Output all "by ref" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + Init += ",&"; + Init += (*I)->getNameAsString(); + } + } + Init += ")"; + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByCopyDecls.clear(); + ImportedBlockDecls.clear(); + + return Init; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteBlocks::RewriteFunctionBody(Stmt *S) { + // Start by rewriting all children. + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) { + Stmt *newStmt = RewriteFunctionBody(CBE->getBody()); + if (newStmt) + *CI = newStmt; + + // We've just rewritten the block body in place. + // Now we snarf the rewritten text and stash it away for later use. + std::string S = Rewrite.getRewritenText(CBE->getSourceRange()); + RewrittenBlockExprs[CBE] = S; + std::string Init = SynthesizeBlockInitExpr(CBE); + // Do the rewrite, using S.size() which contains the rewritten size. + ReplaceText(CBE->getLocStart(), S.size(), Init.c_str(), Init.size()); + } else { + Stmt *newStmt = RewriteFunctionBody(*CI); + if (newStmt) + *CI = newStmt; + } + } + // Handle specific things. + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) + RewriteBlockCall(CE); + } + if (CastExpr *CE = dyn_cast<CastExpr>(S)) { + RewriteCastExpr(CE); + } + if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + + Decl *SD = *DI; + if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) { + if (isBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + } + if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) { + if (isBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + // Handle specific things. + if (BlockDeclRefExpr *BDRE = dyn_cast<BlockDeclRefExpr>(S)) { + if (BDRE->isByRef()) + RewriteBlockDeclRefExpr(BDRE); + } + // Return this stmt unmodified. + return S; +} + +void RewriteBlocks::RewriteFunctionProtoType(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 (isBlockPointerType(*I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteBlocks::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAsPointerType(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteFunctionProtoType(PT->getPointeeType(), ND); +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteBlocks::HandleDeclInMainFile(Decl *D) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteFunctionProtoType(FD->getType(), FD); + + if (CompoundStmt *Body = FD->getBody()) { + CurFunctionDef = FD; + FD->setBody(cast_or_null<CompoundStmt>(RewriteFunctionBody(Body))); + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + CurFunctionDef = 0; + } + return; + } + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + RewriteMethodDecl(MD); + if (Stmt *Body = MD->getBody()) { + CurMethodDef = MD; + RewriteFunctionBody(Body); + InsertBlockLiteralsWithinMethod(MD); + CurMethodDef = 0; + } + } + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (isBlockPointerType(VD->getType())) { + RewriteBlockPointerDecl(VD); + if (VD->getInit()) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(VD->getInit())) { + RewriteFunctionBody(CBE->getBody()); + + // We've just rewritten the block body in place. + // Now we snarf the rewritten text and stash it away for later use. + std::string S = Rewrite.getRewritenText(CBE->getSourceRange()); + RewrittenBlockExprs[CBE] = S; + std::string Init = SynthesizeBlockInitExpr(CBE, VD); + // Do the rewrite, using S.size() which contains the rewritten size. + ReplaceText(CBE->getLocStart(), S.size(), Init.c_str(), Init.size()); + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), + VD->getNameAsCString()); + } else if (CastExpr *CE = dyn_cast<CastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CastExpr *CE = dyn_cast<CastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } + return; + } + if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { + if (isBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + return; + } + if (RecordDecl *RD = dyn_cast<RecordDecl>(D)) { + if (RD->isDefinition()) { + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + FieldDecl *FD = *i; + if (isBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + } + } + return; + } +} diff --git a/clang/tools/clang-cc/RewriteMacros.cpp b/clang/tools/clang-cc/RewriteMacros.cpp new file mode 100644 index 00000000000..809a3b510b2 --- /dev/null +++ b/clang/tools/clang-cc/RewriteMacros.cpp @@ -0,0 +1,236 @@ +//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites macro invocations into their expansions. This gives you +// a macro expanded file that retains comments and #includes. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/ADT/OwningPtr.h" +using namespace clang; + +/// isSameToken - Return true if the two specified tokens start have the same +/// content. +static bool isSameToken(Token &RawTok, Token &PPTok) { + // If two tokens have the same kind and the same identifier info, they are + // obviously the same. + if (PPTok.getKind() == RawTok.getKind() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + // Otherwise, if they are different but have the same identifier info, they + // are also considered to be the same. This allows keywords and raw lexed + // identifiers with the same name to be treated the same. + if (PPTok.getIdentifierInfo() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + return false; +} + + +/// GetNextRawTok - Return the next raw token in the stream, skipping over +/// comments if ReturnComment is false. +static const Token &GetNextRawTok(const std::vector<Token> &RawTokens, + unsigned &CurTok, bool ReturnComment) { + assert(CurTok < RawTokens.size() && "Overran eof!"); + + // If the client doesn't want comments and we have one, skip it. + if (!ReturnComment && RawTokens[CurTok].is(tok::comment)) + ++CurTok; + + return RawTokens[CurTok++]; +} + + +/// LexRawTokensFromMainFile - Lets all the raw tokens from the main file into +/// the specified vector. +static void LexRawTokensFromMainFile(Preprocessor &PP, + std::vector<Token> &RawTokens) { + SourceManager &SM = PP.getSourceManager(); + + // Create a lexer to lex all the tokens of the main file in raw mode. Even + // though it is in raw mode, it will not return comments. + Lexer RawLex(SM.getMainFileID(), SM, PP.getLangOptions()); + + // Switch on comment lexing because we really do want them. + RawLex.SetCommentRetentionState(true); + + Token RawTok; + do { + RawLex.LexFromRawLexer(RawTok); + + // If we have an identifier with no identifier info for our raw token, look + // up the indentifier info. This is important for equality comparison of + // identifier tokens. + if (RawTok.is(tok::identifier) && !RawTok.getIdentifierInfo()) + RawTok.setIdentifierInfo(PP.LookUpIdentifierInfo(RawTok)); + + RawTokens.push_back(RawTok); + } while (RawTok.isNot(tok::eof)); +} + + +/// RewriteMacrosInInput - Implement -rewrite-macros mode. +void clang::RewriteMacrosInInput(Preprocessor &PP,const std::string &InFileName, + const std::string &OutFileName) { + SourceManager &SM = PP.getSourceManager(); + + Rewriter Rewrite; + Rewrite.setSourceMgr(SM); + RewriteBuffer &RB = Rewrite.getEditBuffer(SM.getMainFileID()); + + std::vector<Token> RawTokens; + LexRawTokensFromMainFile(PP, RawTokens); + unsigned CurRawTok = 0; + Token RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + + + // Get the first preprocessing token. + PP.EnterMainSourceFile(); + Token PPTok; + PP.Lex(PPTok); + + // Preprocess the input file in parallel with raw lexing the main file. Ignore + // all tokens that are preprocessed from a file other than the main file (e.g. + // a header). If we see tokens that are in the preprocessed file but not the + // lexed file, we have a macro expansion. If we see tokens in the lexed file + // that aren't in the preprocessed view, we have macros that expand to no + // tokens, or macro arguments etc. + while (RawTok.isNot(tok::eof) || PPTok.isNot(tok::eof)) { + SourceLocation PPLoc = SM.getInstantiationLoc(PPTok.getLocation()); + + // If PPTok is from a different source file, ignore it. + if (!SM.isFromMainFile(PPLoc)) { + PP.Lex(PPTok); + continue; + } + + // If the raw file hits a preprocessor directive, they will be extra tokens + // in the raw file that don't exist in the preprocsesed file. However, we + // choose to preserve them in the output file and otherwise handle them + // specially. + if (RawTok.is(tok::hash) && RawTok.isAtStartOfLine()) { + // If this is a #warning directive or #pragma mark (GNU extensions), + // comment the line out. + if (RawTokens[CurRawTok].is(tok::identifier)) { + const IdentifierInfo *II = RawTokens[CurRawTok].getIdentifierInfo(); + if (!strcmp(II->getName(), "warning")) { + // Comment out #warning. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//", 2); + } else if (!strcmp(II->getName(), "pragma") && + RawTokens[CurRawTok+1].is(tok::identifier) && + !strcmp(RawTokens[CurRawTok+1].getIdentifierInfo()->getName(), + "mark")){ + // Comment out #pragma mark. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//", 2); + } + } + + // Otherwise, if this is a #include or some other directive, just leave it + // in the file by skipping over the line. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + while (!RawTok.isAtStartOfLine() && RawTok.isNot(tok::eof)) + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + continue; + } + + // Okay, both tokens are from the same file. Get their offsets from the + // start of the file. + unsigned PPOffs = SM.getFileOffset(PPLoc); + unsigned RawOffs = SM.getFileOffset(RawTok.getLocation()); + + // If the offsets are the same and the token kind is the same, ignore them. + if (PPOffs == RawOffs && isSameToken(RawTok, PPTok)) { + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + PP.Lex(PPTok); + continue; + } + + // If the PP token is farther along than the raw token, something was + // deleted. Comment out the raw token. + if (RawOffs <= PPOffs) { + // Comment out a whole run of tokens instead of bracketing each one with + // comments. Add a leading space if RawTok didn't have one. + bool HasSpace = RawTok.hasLeadingSpace(); + RB.InsertTextAfter(RawOffs, " /*"+HasSpace, 2+!HasSpace); + unsigned EndPos; + + do { + EndPos = RawOffs+RawTok.getLength(); + + RawTok = GetNextRawTok(RawTokens, CurRawTok, true); + RawOffs = SM.getFileOffset(RawTok.getLocation()); + + if (RawTok.is(tok::comment)) { + // Skip past the comment. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + break; + } + + } while (RawOffs <= PPOffs && !RawTok.isAtStartOfLine() && + (PPOffs != RawOffs || !isSameToken(RawTok, PPTok))); + + RB.InsertTextBefore(EndPos, "*/", 2); + continue; + } + + // Otherwise, there was a replacement an expansion. Insert the new token + // in the output buffer. Insert the whole run of new tokens at once to get + // them in the right order. + unsigned InsertPos = PPOffs; + std::string Expansion; + while (PPOffs < RawOffs) { + Expansion += ' ' + PP.getSpelling(PPTok); + PP.Lex(PPTok); + PPLoc = SM.getInstantiationLoc(PPTok.getLocation()); + PPOffs = SM.getFileOffset(PPLoc); + } + Expansion += ' '; + RB.InsertTextBefore(InsertPos, &Expansion[0], Expansion.size()); + } + + // Create the output file. + llvm::OwningPtr<llvm::raw_ostream> OwnedStream; + llvm::raw_ostream *OutFile; + if (OutFileName == "-") { + OutFile = &llvm::outs(); + } else if (!OutFileName.empty()) { + std::string Err; + OutFile = new llvm::raw_fd_ostream(OutFileName.c_str(), false, Err); + OwnedStream.reset(OutFile); + } else if (InFileName == "-") { + OutFile = &llvm::outs(); + } else { + llvm::sys::Path Path(InFileName); + Path.eraseSuffix(); + Path.appendSuffix("cpp"); + std::string Err; + OutFile = new llvm::raw_fd_ostream(Path.toString().c_str(), false, Err); + OwnedStream.reset(OutFile); + } + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(SM.getMainFileID())) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + fprintf(stderr, "No changes\n"); + } + OutFile->flush(); +} diff --git a/clang/tools/clang-cc/RewriteObjC.cpp b/clang/tools/clang-cc/RewriteObjC.cpp new file mode 100644 index 00000000000..c3cc2c3852a --- /dev/null +++ b/clang/tools/clang-cc/RewriteObjC.cpp @@ -0,0 +1,4588 @@ +//===--- 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 "ASTConsumers.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/TranslationUnit.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; + +static llvm::cl::opt<bool> +SilenceRewriteMacroWarning("Wno-rewrite-macros", llvm::cl::init(false), + llvm::cl::desc("Silence ObjC rewriting warnings")); + +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; + + unsigned NumObjCStringLiterals; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *GetProtocolFunctionDecl; + 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; + + // Needed for header files being rewritten + bool IsHeader; + + std::string InFileName; + std::string OutFileName; + + 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); + + virtual void InitializeTU(TranslationUnit &TU) { + TU.SetOwnsDecls(false); + Initialize(TU.getContext()); + } + + + // Top Level Driver code. + virtual void HandleTopLevelDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteObjC(std::string inFile, std::string outFile, + Diagnostic &D, const LangOptions &LOpts); + + ~RewriteObjC() {} + + virtual void HandleTranslationUnit(TranslationUnit& TU); + + 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 SynthGetProtocolFunctionDecl(); + void SynthSuperContructorFunctionDecl(); + + // Metadata emission. + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + + typedef ObjCCategoryImplDecl::instmeth_iterator instmeth_iterator; + void RewriteObjCMethodsMetaData(instmeth_iterator MethodBegin, + instmeth_iterator MethodEnd, + bool IsInstanceMethod, + const char *prefix, + const char *ClassName, + std::string &Result); + + void RewriteObjCProtocolsMetaData(const ObjCList<ObjCProtocolDecl> + &Protocols, + 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); + void 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 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, std::string outFile, + Diagnostic &D, const LangOptions &LOpts) + : Diags(D), LangOpts(LOpts) { + IsHeader = IsHeaderFile(inFile); + InFileName = inFile; + OutFileName = outFile; + 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::CreateCodeRewriterTest(const std::string& InFile, + const std::string& OutFile, + Diagnostic &Diags, + const LangOptions &LOpts) { + return new RewriteObjC(InFile, OutFile, Diags, LOpts); +} + +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; + GetProtocolFunctionDecl = 0; + ConstantStringClassReference = 0; + NSStringRecord = 0; + CurMethodDef = 0; + CurFunctionDef = 0; + GlobalVarDecl = 0; + SuperStructDecl = 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()); + + // 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::HandleTopLevelDecl(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(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ++DI) + HandleTopLevelDecl(*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()); + + // Add the rewritten getter to trigger meta data generation. An alternate, and + // possibly cleaner approach is to hack RewriteObjCMethodsMetaData() to deal + // with properties explicitly. The following addInstanceMethod() required far + // less code change (and actually models what the rewriter is doing). + if (IMD) + IMD->addInstanceMethod(PD->getGetterMethodDecl()); + else + CID->addInstanceMethod(PD->getGetterMethodDecl()); + + 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()); + + // Add the rewritten setter to trigger meta data generation. + if (IMD) + IMD->addInstanceMethod(PD->getSetterMethodDecl()); + else + CID->addInstanceMethod(PD->getSetterMethodDecl()); +} + +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(), + E = CatDecl->instmeth_end(); I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCCategoryDecl::classmeth_iterator I = CatDecl->classmeth_begin(), + E = CatDecl->classmeth_end(); 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(), + E = PDecl->instmeth_end(); I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(), + E = PDecl->classmeth_end(); 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; + + // Rewrite arguments + ResultStr += "("; + + // invisible arguments + if (OMD->isInstanceMethod()) { + QualType selfTy = Context->getObjCInterfaceType(OMD->getClassInterface()); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.Microsoft) { + if (ObjCSynthesizedStructs.count(OMD->getClassInterface())) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += OMD->getClassInterface()->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCIdType().getAsString(); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(); + ResultStr += " _cmd"; + + // Method arguments. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + ParmVarDecl *PDecl = *PI; + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + if (isTopLevelBlockPointerType(PDecl->getType())) { + // Make sure we convert "t (^)(...)" to "t (*)(...)". + const BlockPointerType *BPT = PDecl->getType()->getAsBlockPointerType(); + Context->getPointerType(BPT->getPointeeType()).getAsStringInternal(Name); + } else + PDecl->getType().getAsStringInternal(Name); + ResultStr += Name; + } + } + if (OMD->isVariadic()) + ResultStr += ", ..."; + ResultStr += ") "; + + if (FPRetType) { + ResultStr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)) { + ResultStr += "("; + for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = FT->getArgType(i).getAsString(); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumArgs()) ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} +void RewriteObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast<ObjCImplementationDecl>(OID); + ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); + + if (IMD) + InsertText(IMD->getLocStart(), "// ", 3); + else + InsertText(CID->getLocStart(), "// ", 3); + + for (ObjCCategoryImplDecl::instmeth_iterator + I = IMD ? IMD->instmeth_begin() : CID->instmeth_begin(), + E = IMD ? IMD->instmeth_end() : CID->instmeth_end(); I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, + ResultStr.c_str(), ResultStr.size()); + } + + for (ObjCCategoryImplDecl::classmeth_iterator + I = IMD ? IMD->classmeth_begin() : CID->classmeth_begin(), + E = IMD ? IMD->classmeth_end() : CID->classmeth_end(); I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, + ResultStr.c_str(), ResultStr.size()); + } + for (ObjCCategoryImplDecl::propimpl_iterator + I = IMD ? IMD->propimpl_begin() : CID->propimpl_begin(), + E = IMD ? IMD->propimpl_end() : CID->propimpl_end(); I != E; ++I) { + RewritePropertyImplDecl(*I, IMD, CID); + } + + if (IMD) + InsertText(IMD->getLocEnd(), "// ", 3); + else + InsertText(CID->getLocEnd(), "// ", 3); +} + +void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + std::string ResultStr; + if (!ObjCForwardDecls.count(ClassDecl)) { + // we haven't seen a forward decl - generate a typedef. + ResultStr = "#ifndef _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "#define _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "typedef struct objc_object "; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += ";\n#endif\n"; + // Mark this typedef as having been generated. + ObjCForwardDecls.insert(ClassDecl); + } + SynthesizeObjCInternalStruct(ClassDecl, ResultStr); + + for (ObjCInterfaceDecl::prop_iterator I = ClassDecl->prop_begin(), + E = ClassDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + for (ObjCInterfaceDecl::instmeth_iterator I = ClassDecl->instmeth_begin(), + E = ClassDecl->instmeth_end(); I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCInterfaceDecl::classmeth_iterator I = ClassDecl->classmeth_begin(), + E = ClassDecl->classmeth_end(); I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndLoc(), 0, "// ", 3); +} + +Stmt *RewriteObjC::RewritePropertySetter(BinaryOperator *BinOp, Expr *newStmt, + SourceRange SrcRange) { + // Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr. + // This allows us to reuse all the fun and games in SynthMessageExpr(). + ObjCPropertyRefExpr *PropRefExpr = dyn_cast<ObjCPropertyRefExpr>(BinOp->getLHS()); + ObjCMessageExpr *MsgExpr; + ObjCPropertyDecl *PDecl = PropRefExpr->getProperty(); + llvm::SmallVector<Expr *, 1> ExprVec; + ExprVec.push_back(newStmt); + + Stmt *Receiver = PropRefExpr->getBase(); + ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(Receiver); + if (PRE && PropGetters[PRE]) { + // This allows us to handle chain/nested property getters. + Receiver = PropGetters[PRE]; + } + MsgExpr = new (Context) ObjCMessageExpr(dyn_cast<Expr>(Receiver), + PDecl->getSetterName(), PDecl->getType(), + PDecl->getSetterMethodDecl(), + SourceLocation(), SourceLocation(), + &ExprVec[0], 1); + Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr); + + // Now do the actual rewrite. + ReplaceStmtWithRange(BinOp, ReplacingStmt, SrcRange); + //delete BinOp; + // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references + // to things that stay around. + Context->Deallocate(MsgExpr); + return ReplacingStmt; +} + +Stmt *RewriteObjC::RewritePropertyGetter(ObjCPropertyRefExpr *PropRefExpr) { + // Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr. + // This allows us to reuse all the fun and games in SynthMessageExpr(). + ObjCMessageExpr *MsgExpr; + ObjCPropertyDecl *PDecl = PropRefExpr->getProperty(); + + Stmt *Receiver = PropRefExpr->getBase(); + + ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(Receiver); + if (PRE && PropGetters[PRE]) { + // This allows us to handle chain/nested property getters. + Receiver = PropGetters[PRE]; + } + MsgExpr = new (Context) ObjCMessageExpr(dyn_cast<Expr>(Receiver), + PDecl->getGetterName(), PDecl->getType(), + PDecl->getGetterMethodDecl(), + SourceLocation(), SourceLocation(), + 0, 0); + + Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr); + + if (!PropParentMap) + PropParentMap = new ParentMap(CurrentBody); + + Stmt *Parent = PropParentMap->getParent(PropRefExpr); + if (Parent && isa<ObjCPropertyRefExpr>(Parent)) { + // We stash away the ReplacingStmt since actually doing the + // replacement/rewrite won't work for nested getters (e.g. obj.p.i) + PropGetters[PropRefExpr] = ReplacingStmt; + // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references + // to things that stay around. + Context->Deallocate(MsgExpr); + return PropRefExpr; // return the original... + } else { + ReplaceStmt(PropRefExpr, ReplacingStmt); + // delete PropRefExpr; elsewhere... + // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references + // to things that stay around. + Context->Deallocate(MsgExpr); + return ReplacingStmt; + } +} + +Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, + SourceLocation OrigStart) { + ObjCIvarDecl *D = IV->getDecl(); + if (CurMethodDef) { + if (const PointerType *pType = IV->getBase()->getType()->getAsPointerType()) { + ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(pType->getPointeeType()); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = 0; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName.c_str()); + RecordDecl *RD = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = new (Context) CStyleCastExpr(castT, IV->getBase(), + castT,SourceLocation(), + SourceLocation()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), + IV->getBase()->getLocEnd(), + castExpr); + if (IV->isFreeIvar() && + CurMethodDef->getClassInterface() == iFaceDecl->getDecl()) { + MemberExpr *ME = new (Context) MemberExpr(PE, true, D, + IV->getLocation(), + D->getType()); + ReplaceStmt(IV, ME); + // delete IV; leak for now, see RewritePropertySetter() usage for more info. + return ME; + } + + ReplaceStmt(IV->getBase(), PE); + // Cannot delete IV->getBase(), since PE points to it. + // Replace the old base with the cast. This is important when doing + // embedded rewrites. For example, [newInv->_container addObject:0]. + IV->setBase(PE); + return IV; + } + } else { // we are outside a method. + assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method"); + + // Explicit ivar refs need to have a cast inserted. + // FIXME: consider sharing some of this code with the code above. + if (const PointerType *pType = IV->getBase()->getType()->getAsPointerType()) { + ObjCInterfaceType *iFaceDecl = dyn_cast<ObjCInterfaceType>(pType->getPointeeType()); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = 0; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName.c_str()); + RecordDecl *RD = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = new (Context) CStyleCastExpr(castT, IV->getBase(), + castT, SourceLocation(), + SourceLocation()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), + IV->getBase()->getLocEnd(), castExpr); + ReplaceStmt(IV->getBase(), PE); + // Cannot delete IV->getBase(), since PE points to it. + // Replace the old base with the cast. This is important when doing + // embedded rewrites. For example, [newInv->_container addObject:0]. + IV->setBase(PE); + return IV; + } + } + return IV; +} + +/// SynthCountByEnumWithState - To print: +/// ((unsigned int (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)items, (unsigned int)16) +/// +void RewriteObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, unsigned int))(void *)objc_msgSend)"; + buf += "\n\t\t"; + buf += "((id)l_collection,\n\t\t"; + buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; + buf += "\n\t\t"; + buf += "&enumState, " + "(id *)items, (unsigned int)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteObjC::RewriteBreakStmt(BreakStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace break with goto __break_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("break"), buf.c_str(), buf.size()); + + return 0; +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteObjC::RewriteContinueStmt(ContinueStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace continue with goto __continue_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("continue"), buf.c_str(), buf.size()); + + return 0; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id items[16]; +/// id l_collection = (id)collection; +/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:items count:16]; +/// if (limit) { +/// unsigned long startMutations = *enumState.mutationsPtr; +/// do { +/// unsigned long counter = 0; +/// do { +/// if (startMutations != *enumState.mutationsPtr) +/// objc_enumerationMutation(l_collection); +/// elem = (type)enumState.itemsPtr[counter++]; +/// stmts; +/// __continue_label: ; +/// } while (counter < limit); +/// } while (limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:items count:16]); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd) { + assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); + assert(isa<ObjCForCollectionStmt>(Stmts.back()) && + "ObjCForCollectionStmt Statement stack mismatch"); + assert(!ObjCBcLabelNo.empty() && + "ObjCForCollectionStmt - Label No stack empty"); + + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *elementName; + std::string elementTypeAsString; + std::string buf; + buf = "\n{\n\t"; + if (DeclStmt *DS = dyn_cast<DeclStmt>(S->getElement())) { + // type elem; + NamedDecl* D = cast<NamedDecl>(DS->getSolitaryDecl()); + QualType ElementType = cast<ValueDecl>(D)->getType(); + elementTypeAsString = ElementType.getAsString(); + buf += elementTypeAsString; + buf += " "; + elementName = D->getNameAsCString(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast<DeclRefExpr>(S->getElement()); + elementName = DR->getDecl()->getNameAsCString(); + elementTypeAsString + = cast<ValueDecl>(DR->getDecl())->getType().getAsString(); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id items[16]; + buf += "id items[16];\n\t"; + // id l_collection = (id) + buf += "id l_collection = (id)"; + // Find start location of 'collection' the hard way! + const char *startCollectionBuf = startBuf; + startCollectionBuf += 3; // skip 'for' + startCollectionBuf = strchr(startCollectionBuf, '('); + startCollectionBuf++; // skip '(' + // find 'in' and skip it. + while (*startCollectionBuf != ' ' || + *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || + (*(startCollectionBuf+3) != ' ' && + *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) + startCollectionBuf++; + startCollectionBuf += 3; + + // Replace: "for (type element in" with string constructed thus far. + ReplaceText(startLoc, startCollectionBuf - startBuf, + buf.c_str(), buf.size()); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getFileLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:items count:16]; + // which is synthesized into: + // unsigned int limit = + // ((unsigned int (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)items, (unsigned int)16); + buf += "unsigned long limit =\n\t\t"; + SynthCountByEnumWithState(buf); + buf += ";\n\t"; + /// if (limit) { + /// unsigned long startMutations = *enumState.mutationsPtr; + /// do { + /// unsigned long counter = 0; + /// do { + /// if (startMutations != *enumState.mutationsPtr) + /// objc_enumerationMutation(l_collection); + /// elem = (type)enumState.itemsPtr[counter++]; + buf += "if (limit) {\n\t"; + buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; + buf += "do {\n\t\t"; + buf += "unsigned long counter = 0;\n\t\t"; + buf += "do {\n\t\t\t"; + buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; + buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; + buf += elementName; + buf += " = ("; + buf += elementTypeAsString; + buf += ")enumState.itemsPtr[counter++];"; + // Replace ')' in for '(' type elem in collection ')' with all of these. + ReplaceText(lparenLoc, 1, buf.c_str(), buf.size()); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while (limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:items count:16]); + /// elem = nil; + /// __break_label: ; + /// } + /// else + /// elem = nil; + /// } + /// + buf = ";\n\t"; + buf += "__continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;"; + buf += "\n\t\t"; + buf += "} while (counter < limit);\n\t"; + buf += "} while (limit = "; + SynthCountByEnumWithState(buf); + buf += ");\n\t"; + buf += elementName; + buf += " = ((id)0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = ((id)0);\n"; + buf += "}\n"; + + // Insert all these *after* the statement body. + if (isa<CompoundStmt>(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getFileLocWithOffset(1); + InsertText(endBodyLoc, buf.c_str(), buf.size()); + } else { + /* Need to treat single statements specially. For example: + * + * for (A *a in b) if (stuff()) break; + * for (A *a in b) xxxyy; + * + * The following code simply scans ahead to the semi to find the actual end. + */ + const char *stmtBuf = SM->getCharacterData(OrigEnd); + const char *semiBuf = strchr(stmtBuf, ';'); + assert(semiBuf && "Can't find ';'"); + SourceLocation endBodyLoc = OrigEnd.getFileLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf.c_str(), buf.size()); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return 0; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @synchronized location"); + + std::string buf; + buf = "objc_sync_enter((id)"; + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf.c_str(), buf.size()); + // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since + // the sync expression is typically a message expression that's already + // been rewritten! (which implies the SourceLocation's are invalid). + SourceLocation endLoc = S->getSynchBody()->getLocStart(); + const char *endBuf = SM->getCharacterData(endLoc); + while (*endBuf != ')') endBuf--; + SourceLocation rparenLoc = startLoc.getFileLocWithOffset(endBuf-startBuf); + buf = ");\n"; + // declare a new scope with two variables, _stack and _rethrow. + buf += "/* @try scope begin */ \n{ struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + ReplaceText(rparenLoc, 1, buf.c_str(), buf.size()); + startLoc = S->getSynchBody()->getLocEnd(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @synchronized block"); + SourceLocation lastCurlyLoc = startLoc; + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + buf += " objc_sync_exit("; + Expr *syncExpr = new (Context) CStyleCastExpr(Context->getObjCIdType(), + S->getSynchExpr(), + Context->getObjCIdType(), + SourceLocation(), + SourceLocation()); + std::string syncExprBufS; + llvm::raw_string_ostream syncExprBuf(syncExprBufS); + syncExpr->printPretty(syncExprBuf); + buf += syncExprBuf.str(); + buf += ");\n"; + buf += " if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}\n"; + buf += "}"; + + ReplaceText(lastCurlyLoc, 1, buf.c_str(), buf.size()); + return 0; +} + +void RewriteObjC::WarnAboutReturnGotoContinueOrBreakStmts(Stmt *S) { + // Perform a bottom up traversal of all children. + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) + WarnAboutReturnGotoContinueOrBreakStmts(*CI); + + if (isa<ReturnStmt>(S) || isa<ContinueStmt>(S) || + isa<BreakStmt>(S) || isa<GotoStmt>(S)) { + Diags.Report(Context->getFullLoc(S->getLocStart()), + TryFinallyContainsReturnDiag); + } + return; +} + +Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + + std::string buf; + // declare a new scope with two variables, _stack and _rethrow. + buf = "/* @try scope begin */ { struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + + ReplaceText(startLoc, 4, buf.c_str(), buf.size()); + + startLoc = S->getTryBody()->getLocEnd(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @try block"); + + SourceLocation lastCurlyLoc = startLoc; + ObjCAtCatchStmt *catchList = S->getCatchStmts(); + if (catchList) { + startLoc = startLoc.getFileLocWithOffset(1); + buf = " /* @catch begin */ else {\n"; + buf += " id _caught = objc_exception_extract(&_stack);\n"; + buf += " objc_exception_try_enter (&_stack);\n"; + buf += " if (_setjmp(_stack.buf))\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += " else { /* @catch continue */"; + + InsertText(startLoc, buf.c_str(), buf.size()); + } else { /* no catch list */ + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf.c_str(), buf.size()); + } + bool sawIdTypedCatch = false; + Stmt *lastCatchBody = 0; + while (catchList) { + ParmVarDecl *catchDecl = catchList->getCatchParamDecl(); + + if (catchList == S->getCatchStmts()) + buf = "if ("; // we are generating code for the first catch clause + else + buf = "else if ("; + startLoc = catchList->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @catch location"); + + const char *lParenLoc = strchr(startBuf, '('); + + if (catchList->hasEllipsis()) { + // Now rewrite the body... + lastCatchBody = catchList->getCatchBody(); + SourceLocation bodyLoc = lastCatchBody->getLocStart(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + assert(*SM->getCharacterData(catchList->getRParenLoc()) == ')' && + "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + buf += "1) { id _tmp = _caught;"; + Rewrite.ReplaceText(startLoc, bodyBuf-startBuf+1, + buf.c_str(), buf.size()); + } else if (catchDecl) { + QualType t = catchDecl->getType(); + if (t == Context->getObjCIdType()) { + buf += "1) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf.c_str(), buf.size()); + sawIdTypedCatch = true; + } else if (const PointerType *pType = t->getAsPointerType()) { + ObjCInterfaceType *cls; // Should be a pointer to a class. + + cls = dyn_cast<ObjCInterfaceType>(pType->getPointeeType().getTypePtr()); + if (cls) { + buf += "objc_exception_match((struct objc_class *)objc_getClass(\""; + buf += cls->getDecl()->getNameAsString(); + buf += "\"), (struct objc_object *)_caught)) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf.c_str(), buf.size()); + } + } + // Now rewrite the body... + lastCatchBody = catchList->getCatchBody(); + SourceLocation rParenLoc = catchList->getRParenLoc(); + SourceLocation bodyLoc = lastCatchBody->getLocStart(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + assert((*rParenBuf == ')') && "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + buf = " = _caught;"; + // Here we replace ") {" with "= _caught;" (which initializes and + // declares the @catch parameter). + ReplaceText(rParenLoc, bodyBuf-rParenBuf+1, buf.c_str(), buf.size()); + } else { + assert(false && "@catch rewrite bug"); + } + // make sure all the catch bodies get rewritten! + catchList = catchList->getNextCatchStmt(); + } + // Complete the catch list... + if (lastCatchBody) { + SourceLocation bodyLoc = lastCatchBody->getLocEnd(); + assert(*SM->getCharacterData(bodyLoc) == '}' && + "bogus @catch body location"); + + // Insert the last (implicit) else clause *before* the right curly brace. + bodyLoc = bodyLoc.getFileLocWithOffset(-1); + buf = "} /* last catch end */\n"; + buf += "else {\n"; + buf += " _rethrow = _caught;\n"; + buf += " objc_exception_try_exit(&_stack);\n"; + buf += "} } /* @catch end */\n"; + if (!S->getFinallyStmt()) + buf += "}\n"; + InsertText(bodyLoc, buf.c_str(), buf.size()); + + // Set lastCurlyLoc + lastCurlyLoc = lastCatchBody->getLocEnd(); + } + if (ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt()) { + startLoc = finalStmt->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @finally start"); + + buf = "/* @finally */"; + ReplaceText(startLoc, 8, buf.c_str(), buf.size()); + + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startLoc = body->getLocStart(); + SourceLocation endLoc = body->getLocEnd(); + assert(*SM->getCharacterData(startLoc) == '{' && + "bogus @finally body location"); + assert(*SM->getCharacterData(endLoc) == '}' && + "bogus @finally body location"); + + startLoc = startLoc.getFileLocWithOffset(1); + buf = " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + InsertText(startLoc, buf.c_str(), buf.size()); + endLoc = endLoc.getFileLocWithOffset(-1); + buf = " if (_rethrow) objc_exception_throw(_rethrow);\n"; + InsertText(endLoc, buf.c_str(), buf.size()); + + // Set lastCurlyLoc + lastCurlyLoc = body->getLocEnd(); + + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoContinueOrBreakStmts(S->getTryBody()); + } else { /* no finally clause - make sure we synthesize an implicit one */ + buf = "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + buf += " if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf.c_str(), buf.size()); + } + // Now emit the final closing curly brace... + lastCurlyLoc = lastCurlyLoc.getFileLocWithOffset(1); + buf = " } /* @try scope end */\n"; + InsertText(lastCurlyLoc, buf.c_str(), buf.size()); + return 0; +} + +Stmt *RewriteObjC::RewriteObjCCatchStmt(ObjCAtCatchStmt *S) { + return 0; +} + +Stmt *RewriteObjC::RewriteObjCFinallyStmt(ObjCAtFinallyStmt *S) { + return 0; +} + +// This can't be done with ReplaceStmt(S, ThrowExpr), since +// the throw expression is typically a message expression that's already +// been rewritten! (which implies the SourceLocation's are invalid). +Stmt *RewriteObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @throw location"); + + std::string buf; + /* void objc_exception_throw(id) __attribute__((noreturn)); */ + if (S->getThrowExpr()) + buf = "objc_exception_throw("; + else // add an implicit argument + buf = "objc_exception_throw(_caught"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf.c_str(), buf.size()); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getFileLocWithOffset(semiBuf-startBuf); + buf = ");"; + ReplaceText(semiLoc, 1, buf.c_str(), buf.size()); + return 0; +} + +Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + QualType StrType = Context->getPointerType(Context->CharTy); + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = StringLiteral::Create(*Context,StrEncoding.c_str(), + StrEncoding.length(), false,StrType, + SourceLocation()); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertySetter() usage for more info. + return Replacement; +} + +Stmt *RewriteObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + llvm::SmallVector<Expr*, 8> SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString().c_str(), + Exp->getSelector().getAsString().size(), + false, argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertySetter() usage for more info. + return SelExp; +} + +CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( + FunctionDecl *FD, Expr **args, unsigned nargs) { + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = FD->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, msgSendType, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = new (Context) ImplicitCastExpr(pToFunc, DRE, + /*isLvalue=*/false); + + const FunctionType *FT = msgSendType->getAsFunctionType(); + + return new (Context) CallExpr(*Context, ICE, args, nargs, FT->getResultType(), + SourceLocation()); +} + +static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, + const char *&startRef, const char *&endRef) { + while (startBuf < endBuf) { + if (*startBuf == '<') + startRef = startBuf; // mark the start. + if (*startBuf == '>') { + if (startRef && *startRef == '<') { + endRef = startBuf; // mark the end. + return true; + } + return false; + } + startBuf++; + } + return false; +} + +static void scanToNextArgument(const char *&argRef) { + int angle = 0; + while (*argRef != ')' && (*argRef != ',' || angle > 0)) { + if (*argRef == '<') + angle++; + else if (*argRef == '>') + angle--; + argRef++; + } + assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); +} + +bool RewriteObjC::needToScanForQualifiers(QualType T) { + + if (T->isObjCQualifiedIdType()) + return true; + + if (const PointerType *pType = T->getAsPointerType()) { + Type *pointeeType = pType->getPointeeType().getTypePtr(); + if (isa<ObjCQualifiedInterfaceType>(pointeeType)) + return true; // we have "Class <Protocol> *". + } + return false; +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { + QualType Type = E->getType(); + if (needToScanForQualifiers(Type)) { + SourceLocation Loc, EndLoc; + + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) { + Loc = ECE->getLParenLoc(); + EndLoc = ECE->getRParenLoc(); + } else { + Loc = E->getLocStart(); + EndLoc = E->getLocEnd(); + } + // This will defend against trying to rewrite synthesized expressions. + if (Loc.isInvalid() || EndLoc.isInvalid()) + return; + + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(EndLoc); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getFileLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getFileLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*", 2); + InsertText(GreaterLoc, "*/", 2); + } + } +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = 0; + if (VarDecl *VD = dyn_cast<VarDecl>(Dcl)) { + Loc = VD->getLocation(); + Type = VD->getType(); + } + else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Dcl)) { + Loc = FD->getLocation(); + // Check for ObjC 'id' and class types that have been adorned with protocol + // information (id<p>, C<p>*). The protocol references need to be rewritten! + const FunctionType *funcType = FD->getType()->getAsFunctionType(); + assert(funcType && "missing function type"); + proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + Type = proto->getResultType(); + } + else + return; + + if (needToScanForQualifiers(Type)) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = SM->getCharacterData(Loc); + const char *startBuf = endBuf; + while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) + startBuf--; // scan backward (from the decl location) for return type. + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getFileLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getFileLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*", 2); + InsertText(GreaterLoc, "*/", 2); + } + } + if (!proto) + return; // most likely, was a variable + // Now check arguments. + const char *startBuf = SM->getCharacterData(Loc); + const char *startFuncBuf = startBuf; + for (unsigned i = 0; i < proto->getNumArgs(); i++) { + if (needToScanForQualifiers(proto->getArgType(i))) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = startBuf; + // scan forward (from the decl location) for argument types. + scanToNextArgument(endBuf); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getFileLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getFileLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*", 2); + InsertText(GreaterLoc, "*/", 2); + } + startBuf = ++endBuf; + } + else { + // If the function name is derived from a macro expansion, then the + // argument buffer will not follow the name. Need to speak with Chris. + while (*startBuf && *startBuf != ')' && *startBuf != ',') + startBuf++; // scan forward (from the decl location) for argument types. + startBuf++; + } + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + llvm::SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType( + Context->CharTy.getQualifiedType(QualType::Const))); + QualType getFuncType = Context->getFunctionType(Context->getObjCSelType(), + &ArgTys[0], ArgTys.size(), + false /*isVariadic*/, 0); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SelGetUidIdent, getFuncType, + FunctionDecl::Extern, false); +} + +// SynthGetProtocolFunctionDecl - Protocol objc_getProtocol(const char *proto); +void RewriteObjC::SynthGetProtocolFunctionDecl() { + IdentifierInfo *SelGetProtoIdent = &Context->Idents.get("objc_getProtocol"); + llvm::SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType( + Context->CharTy.getQualifiedType(QualType::Const))); + QualType getFuncType = Context->getFunctionType(Context->getObjCProtoType(), + &ArgTys[0], ArgTys.size(), + false /*isVariadic*/, 0); + GetProtocolFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SelGetProtoIdent, getFuncType, + FunctionDecl::Extern, false); +} + +void RewriteObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in <objc/objc.h> + if (FD->getIdentifier() && + strcmp(FD->getNameAsCString(), "sel_registerName") == 0) { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +// SynthSuperContructorFunctionDecl - id objc_super(id obj, id super); +void RewriteObjC::SynthSuperContructorFunctionDecl() { + if (SuperContructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + llvm::SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + false, 0); + SuperContructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + llvm::SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/, 0); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + llvm::SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/, 0); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + llvm::SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/, 0); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + llvm::SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/, 0); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + llvm::SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = Context->getFunctionType(Context->DoubleTy, + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/, 0); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + msgSendIdent, msgSendType, + FunctionDecl::Extern, false); +} + +// SynthGetClassFunctionDecl - id objc_getClass(const char *name); +void RewriteObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + llvm::SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType( + Context->CharTy.getQualifiedType(QualType::Const))); + QualType getClassType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + false /*isVariadic*/, 0); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + getClassIdent, getClassType, + FunctionDecl::Extern, false); +} + +// SynthGetMetaClassFunctionDecl - id objc_getClass(const char *name); +void RewriteObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + llvm::SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType( + Context->CharTy.getQualifiedType(QualType::Const))); + QualType getClassType = Context->getFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + false /*isVariadic*/, 0); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + getClassIdent, getClassType, + FunctionDecl::Extern, false); +} + +Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + QualType strType = getConstantStringStructType(); + + std::string S = "__NSConstantStringImpl_"; + + std::string tmpName = InFileName; + unsigned i; + for (i=0; i < tmpName.length(); i++) { + char c = tmpName.at(i); + // replace any non alphanumeric characters with '_'. + if (!isalpha(c) && (c < '0' || c > '9')) + tmpName[i] = '_'; + } + S += tmpName; + S += "_"; + S += utostr(NumObjCStringLiterals++); + + Preamble += "static __NSConstantStringImpl " + S; + Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; + Preamble += "0x000007c8,"; // utf8_str + // The pretty printer for StringLiteral handles escape characters properly. + std::string prettyBufS; + llvm::raw_string_ostream prettyBuf(prettyBufS); + Exp->getString()->printPretty(prettyBuf); + Preamble += prettyBuf.str(); + Preamble += ","; + // The minus 2 removes the begin/end double quotes. + Preamble += utostr(prettyBuf.str().size()-2) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + &Context->Idents.get(S.c_str()), strType, + VarDecl::Static); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, strType, SourceLocation()); + Expr *Unop = new (Context) UnaryOperator(DRE, UnaryOperator::AddrOf, + Context->getPointerType(DRE->getType()), + SourceLocation()); + // cast to NSConstantString * + CastExpr *cast = new (Context) CStyleCastExpr(Exp->getType(), Unop, + Exp->getType(), SourceLocation(), SourceLocation()); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertySetter() usage for more info. + return cast; +} + +ObjCInterfaceDecl *RewriteObjC::isSuperReceiver(Expr *recExpr) { + // check if we are sending a message to 'super' + if (!CurMethodDef || !CurMethodDef->isInstanceMethod()) return 0; + + if (ObjCSuperExpr *Super = dyn_cast<ObjCSuperExpr>(recExpr)) { + const PointerType *PT = Super->getType()->getAsPointerType(); + assert(PT); + ObjCInterfaceType *IT = cast<ObjCInterfaceType>(PT->getPointeeType()); + return IT->getDecl(); + } + return 0; +} + +// struct objc_super { struct objc_object *receiver; struct objc_class *super; }; +QualType RewriteObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), + &Context->Idents.get("objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_class *super; + FieldTypes[1] = Context->getObjCClassType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), 0, + FieldTypes[i], /*BitWidth=*/0, + /*Mutable=*/false)); + } + + SuperStructDecl->completeDefinition(*Context); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), + &Context->Idents.get("__NSConstantStringImpl")); + QualType FieldTypes[4]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // int flags; + FieldTypes[1] = Context->IntTy; + // char *str; + FieldTypes[2] = Context->getPointerType(Context->CharTy); + // long length; + FieldTypes[3] = Context->LongTy; + + // Create fields + for (unsigned i = 0; i < 4; ++i) { + ConstantStringDecl->addDecl(FieldDecl::Create(*Context, + ConstantStringDecl, + SourceLocation(), 0, + FieldTypes[i], + /*BitWidth=*/0, + /*Mutable=*/true)); + } + + ConstantStringDecl->completeDefinition(*Context); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = 0; + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + QualType resultType = OMD->getResultType(); + if (resultType->isStructureType() || resultType->isUnionType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + llvm::SmallVector<Expr*, 8> MsgExprs; + IdentifierInfo *clsName = Exp->getClassName(); + + // Derive/push the receiver/selector, 2 implicit arguments to objc_msgSend(). + if (clsName) { // class message. + // FIXME: We need to fix Sema (and the AST for ObjCMessageExpr) to handle + // the 'super' idiom within a class method. + if (!strcmp(clsName->getName(), "super")) { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *SuperDecl = + CurMethodDef->getClassInterface()->getSuperClass(); + + llvm::SmallVector<Expr*, 4> InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back(new (Context) DeclRefExpr( + CurMethodDef->getSelfDecl(), + Context->getObjCIdType(), + SourceLocation())); + llvm::SmallVector<Expr*, 8> ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + SuperDecl->getIdentifier()->getName(), + SuperDecl->getIdentifier()->getLength(), + false, argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size()); + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using objc_getClass(). + new (Context) CStyleCastExpr(Context->getObjCIdType(), + Cls, Context->getObjCIdType(), + SourceLocation(), SourceLocation())); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.Microsoft) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + superType, SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], + InitExprs.size(), + superType, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UnaryOperator::AddrOf, + Context->getPointerType(SuperRep->getType()), + SourceLocation()); + SuperRep = new (Context) CStyleCastExpr(Context->getPointerType(superType), + SuperRep, Context->getPointerType(superType), + SourceLocation(), SourceLocation()); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = new (Context) InitListExpr(SourceLocation(), + &InitExprs[0], InitExprs.size(), + SourceLocation()); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superType, ILE, + false); + // struct objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UnaryOperator::AddrOf, + Context->getPointerType(SuperRep->getType()), + SourceLocation()); + } + MsgExprs.push_back(SuperRep); + } else { + llvm::SmallVector<Expr*, 8> ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + clsName->getLength(), + false, argType, + SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size()); + MsgExprs.push_back(Cls); + } + } else { // instance message. + Expr *recExpr = Exp->getReceiver(); + + if (ObjCInterfaceDecl *SuperDecl = isSuperReceiver(recExpr)) { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + llvm::SmallVector<Expr*, 4> InitExprs; + + InitExprs.push_back( + new (Context) CStyleCastExpr(Context->getObjCIdType(), + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + Context->getObjCIdType(), + SourceLocation()), + Context->getObjCIdType(), + SourceLocation(), SourceLocation())); // set the 'receiver'. + + llvm::SmallVector<Expr*, 8> ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + SuperDecl->getIdentifier()->getName(), + SuperDecl->getIdentifier()->getLength(), + false, argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size()); + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using objc_getClass(). + new (Context) CStyleCastExpr(Context->getObjCIdType(), + Cls, Context->getObjCIdType(), SourceLocation(), SourceLocation())); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.Microsoft) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + superType, SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], + InitExprs.size(), + superType, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UnaryOperator::AddrOf, + Context->getPointerType(SuperRep->getType()), + SourceLocation()); + SuperRep = new (Context) CStyleCastExpr(Context->getPointerType(superType), + SuperRep, Context->getPointerType(superType), + SourceLocation(), SourceLocation()); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = new (Context) InitListExpr(SourceLocation(), + &InitExprs[0], InitExprs.size(), + SourceLocation()); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superType, ILE, false); + } + MsgExprs.push_back(SuperRep); + } else { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo<Proto> *. + while (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(recExpr)) + recExpr = CE->getSubExpr(); + recExpr = new (Context) CStyleCastExpr(Context->getObjCIdType(), recExpr, + Context->getObjCIdType(), + SourceLocation(), SourceLocation()); + MsgExprs.push_back(recExpr); + } + } + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + llvm::SmallVector<Expr*, 8> SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString().c_str(), + Exp->getSelector().getAsString().size(), + false, argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + MsgExprs.push_back(SelExp); + + // Now push any user supplied arguments. + for (unsigned i = 0; i < Exp->getNumArgs(); i++) { + Expr *userExpr = Exp->getArg(i); + // Make all implicit casts explicit...ICE comes in handy:-) + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) { + // Reuse the ICE type, it is exactly what the doctor ordered. + QualType type = ICE->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : ICE->getType(); + userExpr = new (Context) CStyleCastExpr(type, userExpr, type, SourceLocation(), SourceLocation()); + } + // Make id<P...> cast into an 'id' cast. + else if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(userExpr)) { + if (CE->getType()->isObjCQualifiedIdType()) { + while ((CE = dyn_cast<CStyleCastExpr>(userExpr))) + userExpr = CE->getSubExpr(); + userExpr = new (Context) CStyleCastExpr(Context->getObjCIdType(), + userExpr, Context->getObjCIdType(), + SourceLocation(), SourceLocation()); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. Null out the argument in + // the original expression, since we will delete it below. + Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + llvm::SmallVector<QualType, 8> ArgTypes; + QualType returnType; + + // Push 'id' and 'SEL', the 2 implicit arguments. + if (MsgSendFlavor == MsgSendSuperFunctionDecl) + ArgTypes.push_back(Context->getPointerType(getSuperStructType())); + else + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + // Push any user argument types. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + QualType t = (*PI)->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : (*PI)->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (isTopLevelBlockPointerType(t)) { + const BlockPointerType *BPT = t->getAsBlockPointerType(); + t = Context->getPointerType(BPT->getPointeeType()); + } + ArgTypes.push_back(t); + } + returnType = OMD->getResultType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() : OMD->getResultType(); + } else { + returnType = Context->getObjCIdType(); + } + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, msgSendType, + SourceLocation()); + + // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). + // If we don't do this cast, we get the following bizarre warning/note: + // xx.m:13: warning: function called through a non-compatible type + // xx.m:13: note: if this code is reached, the program will abort + cast = new (Context) CStyleCastExpr(Context->getPointerType(Context->VoidTy), DRE, + Context->getPointerType(Context->VoidTy), + SourceLocation(), SourceLocation()); + + // Now do the "normal" pointer to function cast. + QualType castType = Context->getFunctionType(returnType, + &ArgTypes[0], ArgTypes.size(), + // If we don't have a method decl, force a variadic cast. + Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : true, 0); + castType = Context->getPointerType(castType); + cast = new (Context) CStyleCastExpr(castType, cast, castType, SourceLocation(), SourceLocation()); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + const FunctionType *FT = msgSendType->getAsFunctionType(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], + MsgExprs.size(), + FT->getResultType(), SourceLocation()); + Stmt *ReplacingStmt = CE; + if (MsgSendStretFlavor) { + // We have the method which returns a struct/union. Must also generate + // call to objc_msgSend_stret and hang both varieties on a conditional + // expression which dictate which one to envoke depending on size of + // method's return type. + + // Create a reference to the objc_msgSend_stret() declaration. + DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, msgSendType, + SourceLocation()); + // Need to cast objc_msgSend_stret to "void *" (see above comment). + cast = new (Context) CStyleCastExpr(Context->getPointerType(Context->VoidTy), STDRE, + Context->getPointerType(Context->VoidTy), + SourceLocation(), SourceLocation()); + // Now do the "normal" pointer to function cast. + castType = Context->getFunctionType(returnType, + &ArgTypes[0], ArgTypes.size(), + Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : false, 0); + castType = Context->getPointerType(castType); + cast = new (Context) CStyleCastExpr(castType, cast, castType, SourceLocation(), SourceLocation()); + + // Don't forget the parens to enforce the proper binding. + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + FT = msgSendType->getAsFunctionType(); + CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], + MsgExprs.size(), + FT->getResultType(), SourceLocation()); + + // Build sizeof(returnType) + SizeOfAlignOfExpr *sizeofExpr = new (Context) SizeOfAlignOfExpr(true, + returnType, + Context->getSizeType(), + SourceLocation(), SourceLocation()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. + // For X86 it is more complicated and some kind of target specific routine + // is needed to decide what to do. + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + IntegerLiteral *limit = new (Context) IntegerLiteral(llvm::APInt(IntSize, 8), + Context->IntTy, + SourceLocation()); + BinaryOperator *lessThanExpr = new (Context) BinaryOperator(sizeofExpr, limit, + BinaryOperator::LE, + Context->IntTy, + SourceLocation()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(lessThanExpr, CE, STCE, returnType); + ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), CondExpr); + } + return ReplacingStmt; +} + +Stmt *RewriteObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = SynthMessageExpr(Exp); + + //ReplacingStmt->dump(); + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertySetter() usage for more info. + return ReplacingStmt; +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// call to objc_getProtocol("proto-name"). +Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + if (!GetProtocolFunctionDecl) + SynthGetProtocolFunctionDecl(); + // Create a call to objc_getProtocol("ProtocolName"). + llvm::SmallVector<Expr*, 8> ProtoExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ProtoExprs.push_back(StringLiteral::Create(*Context, + Exp->getProtocol()->getNameAsCString(), + strlen(Exp->getProtocol()->getNameAsCString()), + false, argType, SourceLocation())); + CallExpr *ProtoExp = SynthesizeCallToFunctionDecl(GetProtocolFunctionDecl, + &ProtoExprs[0], + ProtoExprs.size()); + ReplaceStmt(Exp, ProtoExp); + // delete Exp; leak for now, see RewritePropertySetter() usage for more info. + return ProtoExp; + +} + +bool RewriteObjC::BufferContainsPPDirectives(const char *startBuf, + const char *endBuf) { + while (startBuf < endBuf) { + if (*startBuf == '#') { + // Skip whitespace. + for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) + ; + if (!strncmp(startBuf, "if", strlen("if")) || + !strncmp(startBuf, "ifdef", strlen("ifdef")) || + !strncmp(startBuf, "ifndef", strlen("ifndef")) || + !strncmp(startBuf, "define", strlen("define")) || + !strncmp(startBuf, "undef", strlen("undef")) || + !strncmp(startBuf, "else", strlen("else")) || + !strncmp(startBuf, "elif", strlen("elif")) || + !strncmp(startBuf, "endif", strlen("endif")) || + !strncmp(startBuf, "pragma", strlen("pragma")) || + !strncmp(startBuf, "include", strlen("include")) || + !strncmp(startBuf, "import", strlen("import")) || + !strncmp(startBuf, "include_next", strlen("include_next"))) + return true; + } + startBuf++; + } + return false; +} + +/// SynthesizeObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteObjC::SynthesizeObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getNameAsCString() && + "Name missing in SynthesizeObjCInternalStruct"); + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(CDecl)) + return; + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + int NumIvars = CDecl->ivar_size(); + SourceLocation LocStart = CDecl->getLocStart(); + SourceLocation LocEnd = CDecl->getLocEnd(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // If no ivars and no root or if its root, directly or indirectly, + // have no ivars (thus not synthesized) then no need to synthesize this class. + if ((CDecl->isForwardDecl() || NumIvars == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM); + ReplaceText(LocStart, endBuf-startBuf, Result.c_str(), Result.size()); + return; + } + + // FIXME: This has potential of causing problem. If + // SynthesizeObjCInternalStruct is ever called recursively. + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + if (LangOpts.Microsoft) + Result += "_IMPL"; + + if (NumIvars > 0) { + const char *cursor = strchr(startBuf, '{'); + assert((cursor && endBuf) + && "SynthesizeObjCInternalStruct - malformed @interface"); + // If the buffer contains preprocessor directives, we do more fine-grained + // rewrites. This is intended to fix code that looks like (which occurs in + // NSURL.h, for example): + // + // #ifdef XYZ + // @interface Foo : NSObject + // #else + // @interface FooBar : NSObject + // #endif + // { + // int i; + // } + // @end + // + // This clause is segregated to avoid breaking the common case. + if (BufferContainsPPDirectives(startBuf, cursor)) { + SourceLocation L = RCDecl ? CDecl->getSuperClassLoc() : + CDecl->getClassLoc(); + const char *endHeader = SM->getCharacterData(L); + endHeader += Lexer::MeasureTokenLength(L, *SM); + + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + // advance to the end of the referenced protocols. + while (endHeader < cursor && *endHeader != '>') endHeader++; + endHeader++; + } + // rewrite the original header + ReplaceText(LocStart, endHeader-startBuf, Result.c_str(), Result.size()); + } else { + // rewrite the original header *without* disturbing the '{' + ReplaceText(LocStart, cursor-startBuf-1, Result.c_str(), Result.size()); + } + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result = "\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + + // insert the super class structure definition. + SourceLocation OnePastCurly = + LocStart.getFileLocWithOffset(cursor-startBuf+1); + InsertText(OnePastCurly, Result.c_str(), Result.size()); + } + cursor++; // past '{' + + // Now comment out any visibility specifiers. + while (cursor < endBuf) { + if (*cursor == '@') { + SourceLocation atLoc = LocStart.getFileLocWithOffset(cursor-startBuf); + // Skip whitespace. + for (++cursor; cursor[0] == ' ' || cursor[0] == '\t'; ++cursor) + /*scan*/; + + // FIXME: presence of @public, etc. inside comment results in + // this transformation as well, which is still correct c-code. + if (!strncmp(cursor, "public", strlen("public")) || + !strncmp(cursor, "private", strlen("private")) || + !strncmp(cursor, "package", strlen("package")) || + !strncmp(cursor, "protected", strlen("protected"))) + InsertText(atLoc, "// ", 3); + } + // FIXME: If there are cases where '<' is used in ivar declaration part + // of user code, then scan the ivar list and use needToScanForQualifiers + // for type checking. + else if (*cursor == '<') { + SourceLocation atLoc = LocStart.getFileLocWithOffset(cursor-startBuf); + InsertText(atLoc, "/* ", 3); + cursor = strchr(cursor, '>'); + cursor++; + atLoc = LocStart.getFileLocWithOffset(cursor-startBuf); + InsertText(atLoc, " */", 3); + } else if (*cursor == '^') { // rewrite block specifier. + SourceLocation caretLoc = LocStart.getFileLocWithOffset(cursor-startBuf); + ReplaceText(caretLoc, 1, "*", 1); + } + cursor++; + } + // Don't forget to add a ';'!! + InsertText(LocEnd.getFileLocWithOffset(1), ";", 1); + } else { // we don't have any instance variables - insert super struct. + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM); + Result += " {\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n};\n"; + ReplaceText(LocStart, endBuf-startBuf, Result.c_str(), Result.size()); + } + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl)) + assert(false && "struct already synthesize- SynthesizeObjCInternalStruct"); +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +void RewriteObjC::RewriteObjCMethodsMetaData(instmeth_iterator MethodBegin, + instmeth_iterator MethodEnd, + bool IsInstanceMethod, + const char *prefix, + const char *ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + static bool objc_impl_method = false; + if (!objc_impl_method) { + /* struct _objc_method { + SEL _cmd; + char *method_types; + void *_imp; + } + */ + Result += "\nstruct _objc_method {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + objc_impl_method = true; + } + + // Build _objc_method_list for class's methods if needed + + /* struct { + struct _objc_method_list *next_method; + int method_count; + struct _objc_method method_list[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(MethodEnd-MethodBegin); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; + Result += "_METHODS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __"; + Result += IsInstanceMethod ? "inst" : "cls"; + Result += "_meth\")))= "; + Result += "{\n\t0, " + utostr(MethodEnd-MethodBegin) + "\n"; + + Result += "\t,{{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { + Result += "\t ,{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + } + Result += "\t }\n};\n"; +} + +/// RewriteObjCProtocolsMetaData - Rewrite protocols meta-data. +void RewriteObjC:: +RewriteObjCProtocolsMetaData(const ObjCList<ObjCProtocolDecl> &Protocols, + const char *prefix, + const char *ClassName, + std::string &Result) { + static bool objc_protocol_methods = false; + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) { + ObjCProtocolDecl *PDecl = Protocols[i]; + // Output struct protocol_methods holder of method selector and type. + if (!objc_protocol_methods && !PDecl->isForwardDecl()) { + /* struct protocol_methods { + SEL _cmd; + char *method_types; + } + */ + Result += "\nstruct protocol_methods {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "};\n"; + + objc_protocol_methods = true; + } + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl)) + continue; + + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + unsigned NumMethods = std::distance(PDecl->instmeth_begin(), + PDecl->instmeth_end()); + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct protocol_methods protocols["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_inst_meth\")))= " + "{\n\t" + utostr(NumMethods) + "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::instmeth_iterator I = PDecl->instmeth_begin(), + E = PDecl->instmeth_end(); I != E; ++I) { + if (I == PDecl->instmeth_begin()) + Result += "\t ,{{(SEL)\""; + else + Result += "\t ,{(SEL)\""; + Result += (*I)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output class methods declared in this protocol. + unsigned NumMethods = std::distance(PDecl->classmeth_begin(), + PDecl->classmeth_end()); + if (NumMethods > 0) { + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct protocol_methods protocols["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t"; + Result += utostr(NumMethods); + Result += "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(), + E = PDecl->classmeth_end(); I != E; ++I) { + if (I == PDecl->classmeth_begin()) + Result += "\t ,{{(SEL)\""; + else + Result += "\t ,{(SEL)\""; + Result += (*I)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output: + /* struct _objc_protocol { + // Objective-C 1.0 extensions + struct _objc_protocol_extension *isa; + char *protocol_name; + struct _objc_protocol **protocol_list; + struct _objc_protocol_method_list *instance_methods; + struct _objc_protocol_method_list *class_methods; + }; + */ + static bool objc_protocol = false; + if (!objc_protocol) { + Result += "\nstruct _objc_protocol {\n"; + Result += "\tstruct _objc_protocol_extension *isa;\n"; + Result += "\tchar *protocol_name;\n"; + Result += "\tstruct _objc_protocol **protocol_list;\n"; + Result += "\tstruct _objc_protocol_method_list *instance_methods;\n"; + Result += "\tstruct _objc_protocol_method_list *class_methods;\n"; + Result += "};\n"; + + objc_protocol = true; + } + + Result += "\nstatic struct _objc_protocol _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __protocol\")))= " + "{\n\t0, \""; + Result += PDecl->getNameAsString(); + Result += "\", 0, "; + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += ", "; + } + else + Result += "0, "; + if (PDecl->classmeth_begin() != PDecl->classmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += "\n"; + } + else + Result += "0\n"; + Result += "};\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl)) + assert(false && "protocol already synthesized"); + } + // Output the top lovel protocol meta-data for the class. + /* struct _objc_protocol_list { + struct _objc_protocol_list *next; + int protocol_count; + struct _objc_protocol *class_protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_protocol_list *next;\n"; + Result += "\tint protocol_count;\n"; + Result += "\tstruct _objc_protocol *class_protocols["; + Result += utostr(Protocols.size()); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += "_PROTOCOLS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t0, "; + Result += utostr(Protocols.size()); + Result += "\n"; + + Result += "\t,{&_OBJC_PROTOCOL_"; + Result += Protocols[0]->getNameAsString(); + Result += " \n"; + + for (unsigned i = 1; i != Protocols.size(); i++) { + Result += "\t ,&_OBJC_PROTOCOL_"; + Result += Protocols[i]->getNameAsString(); + Result += "\n"; + } + Result += "\t }\n};\n"; +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl; + for (CDecl = ClassDecl->getCategoryList(); CDecl; + CDecl = CDecl->getNextClassCategory()) + if (CDecl->getIdentifier() == IDecl->getIdentifier()) + break; + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += '_'; + FullCategoryName += IDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + RewriteObjCMethodsMetaData(IDecl->instmeth_begin(), IDecl->instmeth_end(), + true, "CATEGORY_", FullCategoryName.c_str(), + Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "CATEGORY_", FullCategoryName.c_str(), + Result); + + // Protocols referenced in class declaration? + // Null CDecl is case of a category implementation with no category interface + if (CDecl) + RewriteObjCProtocolsMetaData(CDecl->getReferencedProtocols(), "CATEGORY", + FullCategoryName.c_str(), Result); + + /* struct _objc_category { + char *category_name; + char *class_name; + struct _objc_method_list *instance_methods; + struct _objc_method_list *class_methods; + struct _objc_protocol_list *protocols; + // Objective-C 1.0 extensions + uint32_t size; // sizeof (struct _objc_category) + struct _objc_property_list *instance_properties; // category's own + // @property decl. + }; + */ + + static bool objc_category = false; + if (!objc_category) { + Result += "\nstruct _objc_category {\n"; + Result += "\tchar *category_name;\n"; + Result += "\tchar *class_name;\n"; + Result += "\tstruct _objc_method_list *instance_methods;\n"; + Result += "\tstruct _objc_method_list *class_methods;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tunsigned int size;\n"; + Result += "\tstruct _objc_property_list *instance_properties;\n"; + Result += "};\n"; + objc_category = true; + } + Result += "\nstatic struct _objc_category _OBJC_CATEGORY_"; + Result += FullCategoryName; + Result += " __attribute__ ((used, section (\"__OBJC, __category\")))= {\n\t\""; + Result += IDecl->getNameAsString(); + Result += "\"\n\t, \""; + Result += ClassDecl->getNameAsString(); + Result += "\"\n"; + + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_INSTANCE_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_CLASS_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + + if (CDecl && CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t, (struct _objc_protocol_list *)&_OBJC_CATEGORY_PROTOCOLS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + Result += "\t, sizeof(struct _objc_category), 0\n};\n"; +} + +/// SynthesizeIvarOffsetComputation - This rutine synthesizes computation of +/// ivar offset. +void RewriteObjC::SynthesizeIvarOffsetComputation(ObjCImplementationDecl *IDecl, + ObjCIvarDecl *ivar, + std::string &Result) { + if (ivar->isBitField()) { + // FIXME: The hack below doesn't work for bitfields. For now, we simply + // place all bitfields at offset 0. + Result += "0"; + } else { + Result += "__OFFSETOFIVAR__(struct "; + Result += IDecl->getNameAsString(); + if (LangOpts.Microsoft) + Result += "_IMPL"; + Result += ", "; + Result += ivar->getNameAsString(); + Result += ")"; + } +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + +void RewriteObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explictly declared @interface's are already synthesized. + if (CDecl->ImplicitInterfaceDecl()) { + // FIXME: Implementation of a class with no @interface (legacy) doese not + // produce correct synthesis as yet. + SynthesizeObjCInternalStruct(CDecl, Result); + } + + // Build _objc_ivar_list metadata for classes ivars if needed + unsigned NumIvars = !IDecl->ivar_empty() + ? IDecl->ivar_size() + : (CDecl ? CDecl->ivar_size() : 0); + if (NumIvars > 0) { + static bool objc_ivar = false; + if (!objc_ivar) { + /* struct _objc_ivar { + char *ivar_name; + char *ivar_type; + int ivar_offset; + }; + */ + Result += "\nstruct _objc_ivar {\n"; + Result += "\tchar *ivar_name;\n"; + Result += "\tchar *ivar_type;\n"; + Result += "\tint ivar_offset;\n"; + Result += "};\n"; + + objc_ivar = true; + } + + /* struct { + int ivar_count; + struct _objc_ivar ivar_list[nIvars]; + }; + */ + Result += "\nstatic struct {\n"; + Result += "\tint ivar_count;\n"; + Result += "\tstruct _objc_ivar ivar_list["; + Result += utostr(NumIvars); + Result += "];\n} _OBJC_INSTANCE_VARIABLES_"; + Result += IDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __instance_vars\")))= " + "{\n\t"; + Result += utostr(NumIvars); + Result += "\n"; + + ObjCInterfaceDecl::ivar_iterator IVI, IVE; + if (!IDecl->ivar_empty()) { + IVI = IDecl->ivar_begin(); + IVE = IDecl->ivar_end(); + } else { + IVI = CDecl->ivar_begin(); + IVE = CDecl->ivar_end(); + } + Result += "\t,{{\""; + Result += (*IVI)->getNameAsString(); + Result += "\", \""; + std::string StrEncoding; + Context->getObjCEncodingForType((*IVI)->getType(), StrEncoding); + Result += StrEncoding; + Result += "\", "; + SynthesizeIvarOffsetComputation(IDecl, *IVI, Result); + Result += "}\n"; + for (++IVI; IVI != IVE; ++IVI) { + Result += "\t ,{\""; + Result += (*IVI)->getNameAsString(); + Result += "\", \""; + std::string StrEncoding; + Context->getObjCEncodingForType((*IVI)->getType(), StrEncoding); + Result += StrEncoding; + Result += "\", "; + SynthesizeIvarOffsetComputation(IDecl, (*IVI), Result); + Result += "}\n"; + } + + Result += "\t }\n};\n"; + } + + // Build _objc_method_list for class's instance methods if needed + RewriteObjCMethodsMetaData(IDecl->instmeth_begin(), IDecl->instmeth_end(), + true, "", IDecl->getNameAsCString(), Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "", IDecl->getNameAsCString(), Result); + + // Protocols referenced in class declaration? + RewriteObjCProtocolsMetaData(CDecl->getReferencedProtocols(), + "CLASS", CDecl->getNameAsCString(), Result); + + + // Declaration of class/meta-class metadata + /* struct _objc_class { + struct _objc_class *isa; // or const char *root_class_name when metadata + const char *super_class_name; + char *name; + long version; + long info; + long instance_size; + struct _objc_ivar_list *ivars; + struct _objc_method_list *methods; + struct objc_cache *cache; + struct objc_protocol_list *protocols; + const char *ivar_layout; + struct _objc_class_ext *ext; + }; + */ + static bool objc_class = false; + if (!objc_class) { + Result += "\nstruct _objc_class {\n"; + Result += "\tstruct _objc_class *isa;\n"; + Result += "\tconst char *super_class_name;\n"; + Result += "\tchar *name;\n"; + Result += "\tlong version;\n"; + Result += "\tlong info;\n"; + Result += "\tlong instance_size;\n"; + Result += "\tstruct _objc_ivar_list *ivars;\n"; + Result += "\tstruct _objc_method_list *methods;\n"; + Result += "\tstruct objc_cache *cache;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tconst char *ivar_layout;\n"; + Result += "\tstruct _objc_class_ext *ext;\n"; + Result += "};\n"; + objc_class = true; + } + + // Meta-class metadata generation. + ObjCInterfaceDecl *RootClass = 0; + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + while (SuperClass) { + RootClass = SuperClass; + SuperClass = SuperClass->getSuperClass(); + } + SuperClass = CDecl->getSuperClass(); + + Result += "\nstatic struct _objc_class _OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __meta_class\")))= " + "{\n\t(struct _objc_class *)\""; + Result += (RootClass ? RootClass->getNameAsString() : CDecl->getNameAsString()); + Result += "\""; + + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // Set 'ivars' field for root class to 0. ObjC1 runtime does not use it. + // 'info' field is initialized to CLS_META(2) for metaclass + Result += ", 0,2, sizeof(struct _objc_class), 0"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\n\t, (struct _objc_method_list *)&_OBJC_CLASS_METHODS_"; + Result += IDecl->getNameAsString(); + Result += "\n"; + } + else + Result += ", 0\n"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t,0, (struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ",0,0\n"; + } + else + Result += "\t,0,0,0,0\n"; + Result += "};\n"; + + // class metadata generation. + Result += "\nstatic struct _objc_class _OBJC_CLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __class\")))= " + "{\n\t&_OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // 'info' field is initialized to CLS_CLASS(1) for class + Result += ", 0,1"; + if (!ObjCSynthesizedStructs.count(CDecl)) + Result += ",0"; + else { + // class has size. Must synthesize its size. + Result += ",sizeof(struct "; + Result += CDecl->getNameAsString(); + if (LangOpts.Microsoft) + Result += "_IMPL"; + Result += ")"; + } + if (NumIvars > 0) { + Result += ", (struct _objc_ivar_list *)&_OBJC_INSTANCE_VARIABLES_"; + Result += CDecl->getNameAsString(); + Result += "\n\t"; + } + else + Result += ",0"; + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += ", (struct _objc_method_list *)&_OBJC_INSTANCE_METHODS_"; + Result += CDecl->getNameAsString(); + Result += ", 0\n\t"; + } + else + Result += ",0,0"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += ", (struct _objc_protocol_list*)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ", 0,0\n"; + } + else + Result += ",0,0,0\n"; + Result += "};\n"; +} + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) + RewriteImplementationDecl(ClassImplementation[i]); + + for (int i = 0; i < CatDefCount; i++) + RewriteImplementationDecl(CategoryImplementation[i]); +} + +void RewriteObjC::SynthesizeMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // This is needed for determining instance variable offsets. + Result += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)\n"; + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + // Write objc_symtab metadata + /* + struct _objc_symtab + { + long sel_ref_cnt; + SEL *refs; + short cls_def_cnt; + short cat_def_cnt; + void *defs[cls_def_cnt + cat_def_cnt]; + }; + */ + + Result += "\nstruct _objc_symtab {\n"; + Result += "\tlong sel_ref_cnt;\n"; + Result += "\tSEL *refs;\n"; + Result += "\tshort cls_def_cnt;\n"; + Result += "\tshort cat_def_cnt;\n"; + Result += "\tvoid *defs[" + utostr(ClsDefCount + CatDefCount)+ "];\n"; + Result += "};\n\n"; + + Result += "static struct _objc_symtab " + "_OBJC_SYMBOLS __attribute__((used, section (\"__OBJC, __symbols\")))= {\n"; + Result += "\t0, 0, " + utostr(ClsDefCount) + + ", " + utostr(CatDefCount) + "\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t,&_OBJC_CLASS_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += "\n"; + } + + for (int i = 0; i < CatDefCount; i++) { + Result += "\t,&_OBJC_CATEGORY_"; + Result += CategoryImplementation[i]->getClassInterface()->getNameAsString(); + Result += "_"; + Result += CategoryImplementation[i]->getNameAsString(); + Result += "\n"; + } + + Result += "};\n\n"; + + // Write objc_module metadata + + /* + struct _objc_module { + long version; + long size; + const char *name; + struct _objc_symtab *symtab; + } + */ + + Result += "\nstruct _objc_module {\n"; + Result += "\tlong version;\n"; + Result += "\tlong size;\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _objc_symtab *symtab;\n"; + Result += "};\n\n"; + Result += "static struct _objc_module " + "_OBJC_MODULES __attribute__ ((used, section (\"__OBJC, __module_info\")))= {\n"; + Result += "\t" + utostr(OBJC_ABI_VERSION) + + ", sizeof(struct _objc_module), \"\", &_OBJC_SYMBOLS\n"; + Result += "};\n\n"; + + if (LangOpts.Microsoft) { + Result += "#pragma section(\".objc_module_info$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_module_info$B\")\n"; + Result += "static struct _objc_module *_POINTER_OBJC_MODULES = "; + Result += "&_OBJC_MODULES;\n"; + Result += "#pragma data_seg(pop)\n\n"; + } +} + +std::string RewriteObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + const char *funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getResultType(); + std::string StructRef = "struct " + Tag; + std::string S = "static " + RT.getAsString() + " __" + + funcName + "_" + "block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa<FunctionNoProtoType>(AFT)) { + // No user-supplied arguments. Still need to pass in a pointer to the + // block (to reference imported block decl refs). + S += "(" + StructRef + " *__cself)"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast<FunctionProtoType>(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + (*AI)->getType().getAsStringInternal(ParamStr); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + Context->getPointerType((*I)->getType()).getAsStringInternal(Name); + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) + S += "struct __block_impl *"; + else + (*I)->getType().getAsStringInternal(Name); + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + const char *funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + S += "_Block_object_assign((void*)&dst->"; + S += (*I)->getNameAsString(); + S += ", (void*)src->"; + S += (*I)->getNameAsString(); + S += ", 3/*BLOCK_FIELD_IS_OBJECT*/);}"; + } + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + S += "_Block_object_dispose((void*)src->"; + S += (*I)->getNameAsString(); + S += ", 3/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + bool hasCopyDisposeHelpers) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + + if (hasCopyDisposeHelpers) + S += " void *copy;\n void *dispose;\n"; + + Constructor += "(void *fp"; + + if (hasCopyDisposeHelpers) + Constructor += ", void *copyHelp, void *disposeHelp"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + (*I)->getType().getAsStringInternal(FieldName); + (*I)->getType().getAsStringInternal(ArgName); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + Context->getPointerType((*I)->getType()).getAsStringInternal(FieldName); + Context->getPointerType((*I)->getType()).getAsStringInternal(ArgName); + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + // FIXME: handle NSConcreteGlobalBlock. + Constructor += ", int flags=0) {\n"; + Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; + Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + if (hasCopyDisposeHelpers) + Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; + + // Initialize all "by copy" arguments. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + Constructor += " "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + " = (struct __block_impl *)_"; + else + Constructor += Name + " = _"; + Constructor += Name + ";\n"; + } + // Initialize all "by ref" arguments. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + Constructor += " "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + " = (struct __block_impl *)_"; + else + Constructor += Name + " = _"; + Constructor += Name + ";\n"; + } + } else { + // Finish writing the constructor. + // FIXME: handle NSConcreteGlobalBlock. + Constructor += ", int flags=0) {\n"; + Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; + Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + if (hasCopyDisposeHelpers) + Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +void RewriteObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + const char *FunName) { + // Insert closures that were part of the function. + for (unsigned i = 0; i < Blocks.size(); i++) { + + CollectBlockDeclRefInfo(Blocks[i]); + + std::string Tag = "__" + std::string(FunName) + "_block_impl_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], Tag, + ImportedBlockDecls.size() > 0); + + InsertText(FunLocStart, CI.c_str(), CI.size()); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, Tag); + + InsertText(FunLocStart, CF.c_str(), CF.size()); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, Tag); + InsertText(FunLocStart, HF.c_str(), HF.size()); + } + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByCopyDecls.clear(); + BlockCallExprs.clear(); + ImportedBlockDecls.clear(); + } + Blocks.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const char *FuncName = FD->getNameAsCString(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + //SourceLocation FunLocStart = MD->getLocStart(); + // FIXME: This hack works around a bug in Rewrite.InsertText(). + SourceLocation FunLocStart = MD->getLocStart().getFileLocWithOffset(-1); + std::string FuncName = MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = FuncName.find(":", loc)) != std::string::npos) + FuncName.replace(loc, 1, "_"); + + SynthesizeBlockLiterals(FunLocStart, FuncName.c_str()); +} + +void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(S)) + // FIXME: Handle enums. + if (!isa<FunctionDecl>(CDRE->getDecl())) + BlockDeclRefs.push_back(CDRE); + return; +} + +void RewriteObjC::GetBlockCallExprs(Stmt *S) { + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockCallExprs(CBE->getBody()); + else + GetBlockCallExprs(*CI); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + BlockCallExprs[dyn_cast<BlockDeclRefExpr>(CE->getCallee())] = CE; + } + } + return; +} + +Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp) { + // Navigate to relevant type information. + const char *closureName = 0; + const BlockPointerType *CPT = 0; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp->getCallee())) { + closureName = DRE->getDecl()->getNameAsCString(); + CPT = DRE->getType()->getAsBlockPointerType(); + } else if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(Exp->getCallee())) { + closureName = CDRE->getDecl()->getNameAsCString(); + CPT = CDRE->getType()->getAsBlockPointerType(); + } else if (MemberExpr *MExpr = dyn_cast<MemberExpr>(Exp->getCallee())) { + closureName = MExpr->getMemberDecl()->getNameAsCString(); + CPT = MExpr->getType()->getAsBlockPointerType(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAsFunctionType(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + + RecordDecl *RD = RecordDecl::Create(*Context, TagDecl::TK_struct, TUDecl, + SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + llvm::SmallVector<QualType, 8> ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) { + QualType t = *I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (isTopLevelBlockPointerType(t)) { + const BlockPointerType *BPT = t->getAsBlockPointerType(); + t = Context->getPointerType(BPT->getPointeeType()); + } + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType = Context->getFunctionType(Exp->getType(), + &ArgTypes[0], ArgTypes.size(), false/*no variadic*/, 0); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = new (Context) CStyleCastExpr(PtrBlock, Exp->getCallee(), + PtrBlock, SourceLocation(), + SourceLocation()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + BlkCast); + //PE->dump(); + + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + &Context->Idents.get("FuncPtr"), Context->VoidPtrTy, + /*BitWidth=*/0, /*Mutable=*/true); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType()); + + CastExpr *FunkCast = new (Context) CStyleCastExpr(PtrToFuncCastType, ME, + PtrToFuncCastType, + SourceLocation(), + SourceLocation()); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + llvm::SmallVector<Expr*, 8> BlkExprs; + // Add the implicit argument. + BlkExprs.push_back(BlkCast); + // Add the user arguments. + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + BlkExprs.push_back(*I); + } + CallExpr *CE = new (Context) CallExpr(*Context, PE, &BlkExprs[0], + BlkExprs.size(), + Exp->getType(), SourceLocation()); + return CE; +} + +void RewriteObjC::RewriteBlockCall(CallExpr *Exp) { + Stmt *BlockCall = SynthesizeBlockCall(Exp); + ReplaceStmt(Exp, BlockCall); +} + +void RewriteObjC::RewriteBlockDeclRefExpr(BlockDeclRefExpr *BDRE) { + // FIXME: Add more elaborate code generation required by the ABI. + Expr *DerefExpr = new (Context) UnaryOperator(BDRE, UnaryOperator::Deref, + Context->getPointerType(BDRE->getType()), + SourceLocation()); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), DerefExpr); + ReplaceStmt(BDRE, PE); +} + +void RewriteObjC::RewriteCastExpr(CStyleCastExpr *CE) { + SourceLocation LocStart = CE->getLParenLoc(); + SourceLocation LocEnd = CE->getRParenLoc(); + + // Need to avoid trying to rewrite synthesized casts. + if (LocStart.isInvalid()) + return; + // Need to avoid trying to rewrite casts contained in macros. + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getFileLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*", 1); + break; + } + } + return; +} + +void RewriteObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getFileLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getFileLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*", 1); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAsPointerType(); + if (PT) { + FTP = PT->getPointeeType()->getAsFunctionProtoType(); + } else { + const BlockPointerType *BPT = QT->getAsBlockPointerType(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAsFunctionProtoType(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) + if (isTopLevelBlockPointerType(*I)) + return true; + } + return false; +} + +void RewriteObjC::GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteObjC::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast<VarDecl>(ND)) + DeclT = VD->getType(); + else if (TypedefDecl *TDD = dyn_cast<TypedefDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + assert(0 && "RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + DeclLoc = DeclLoc.getFileLocWithOffset(startBuf-endBuf); + ReplaceText(DeclLoc, 1, "*", 1); + } + if (PointerTypeTakesAnyBlockArguments(DeclT)) { + // Replace the '^' with '*' for arguments. + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') { + SourceLocation CaretLoc = DeclLoc.getFileLocWithOffset(argListBegin-startBuf); + ReplaceText(CaretLoc, 1, "*", 1); + } + argListBegin++; + } + } + return; +} + +void RewriteObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->isByRef()) + BlockByCopyDecls.insert(BlockDeclRefs[i]->getDecl()); + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->isByRef()) { + BlockByRefDecls.insert(BlockDeclRefs[i]->getDecl()); + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getType()->isBlockPointerType()) { + GetBlockCallExprs(BlockDeclRefs[i]); + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } + } +} + +FunctionDecl *RewriteObjC::SynthBlockInitFunctionDecl(const char *name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl,SourceLocation(), + ID, FType, FunctionDecl::Extern, false, + false); +} + +Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp) { + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) { + FuncName = CurMethodDef->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = FuncName.find(":", loc)) != std::string::npos) + FuncName.replace(loc, 1, "_"); + } else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType FType = Context->getPointerType(QualType(Exp->getFunctionType(),0)); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a contructor call... + FD = SynthBlockInitFunctionDecl(Tag.c_str()); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, FType, SourceLocation()); + + llvm::SmallVector<Expr*, 4> InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func.c_str()); + DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, FD->getType(), + SourceLocation()); + CastExpr *castExpr = new (Context) CStyleCastExpr(Context->VoidPtrTy, Arg, + Context->VoidPtrTy, SourceLocation(), + SourceLocation()); + InitExprs.push_back(castExpr); + + if (ImportedBlockDecls.size()) { + std::string Buf = "__" + FuncName + "_block_copy_" + BlockNumber; + FD = SynthBlockInitFunctionDecl(Buf.c_str()); + Arg = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + castExpr = new (Context) CStyleCastExpr(Context->VoidPtrTy, Arg, + Context->VoidPtrTy, SourceLocation(), + SourceLocation()); + InitExprs.push_back(castExpr); + + Buf = "__" + FuncName + "_block_dispose_" + BlockNumber; + FD = SynthBlockInitFunctionDecl(Buf.c_str()); + Arg = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + castExpr = new (Context) CStyleCastExpr(Context->VoidPtrTy, Arg, + Context->VoidPtrTy, SourceLocation(), + SourceLocation()); + InitExprs.push_back(castExpr); + } + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getNameAsCString()); + Exp = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getNameAsCString()); + Arg = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + Exp = new (Context) CStyleCastExpr(Context->VoidPtrTy, Arg, + Context->VoidPtrTy, SourceLocation(), + SourceLocation()); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getNameAsCString()); + Exp = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + FD = SynthBlockInitFunctionDecl((*I)->getNameAsCString()); + Exp = new (Context) DeclRefExpr(FD, FD->getType(), SourceLocation()); + Exp = new (Context) UnaryOperator(Exp, UnaryOperator::AddrOf, + Context->getPointerType(Exp->getType()), + SourceLocation()); + InitExprs.push_back(Exp); + } + } + NewRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], InitExprs.size(), + FType, SourceLocation()); + NewRep = new (Context) UnaryOperator(NewRep, UnaryOperator::AddrOf, + Context->getPointerType(NewRep->getType()), + SourceLocation()); + NewRep = new (Context) CStyleCastExpr(FType, NewRep, FType, SourceLocation(), + SourceLocation()); + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByCopyDecls.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +// This is run as a first "pass" prior to RewriteFunctionBodyOrGlobalInitializer(). +// The allows the main rewrite loop to associate all ObjCPropertyRefExprs with +// their respective BinaryOperator. Without this knowledge, we'd need to rewrite +// the ObjCPropertyRefExpr twice (once as a getter, and later as a setter). +// Since the rewriter isn't capable of rewriting rewritten code, it's important +// we get this right. +void RewriteObjC::CollectPropertySetters(Stmt *S) { + // Perform a bottom up traversal of all children. + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) + CollectPropertySetters(*CI); + + if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) { + if (BinOp->isAssignmentOp()) { + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(BinOp->getLHS())) + PropSetters[PRE] = BinOp; + } + } +} + +Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) + Stmts.push_back(S); + else if (isa<ObjCForCollectionStmt>(S)) { + Stmts.push_back(S); + ObjCBcLabelNo.push_back(++BcLabelCount); + } + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); + CI != E; ++CI) + if (*CI) { + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(*CI); + if (newStmt) + *CI = newStmt; + } + + if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + // Rewrite the block body in place. + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewritenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE); + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) + return RewriteObjCIvarRefExpr(IvarRefExpr, OrigStmtRange.getBegin()); + + if (ObjCPropertyRefExpr *PropRefExpr = dyn_cast<ObjCPropertyRefExpr>(S)) { + BinaryOperator *BinOp = PropSetters[PropRefExpr]; + if (BinOp) { + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to rewrite the right hand side prior to rewriting the setter. + DisableReplaceStmt = true; + // Save the source range. Even if we disable the replacement, the + // rewritten node will have been inserted into the tree. If the synthesized + // node is at the 'end', the rewriter will fail. Consider this: + // self.errorHandler = handler ? handler : + // ^(NSURL *errorURL, NSError *error) { return (BOOL)1; }; + SourceRange SrcRange = BinOp->getSourceRange(); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(BinOp->getRHS()); + DisableReplaceStmt = false; + // + // Unlike the main iterator, we explicily avoid changing 'BinOp'. If + // we changed the RHS of BinOp, the rewriter would fail (since it needs + // to see the original expression). Consider this example: + // + // Foo *obj1, *obj2; + // + // obj1.i = [obj2 rrrr]; + // + // 'BinOp' for the previous expression looks like: + // + // (BinaryOperator 0x231ccf0 'int' '=' + // (ObjCPropertyRefExpr 0x231cc70 'int' Kind=PropertyRef Property="i" + // (DeclRefExpr 0x231cc50 'Foo *' Var='obj1' 0x231cbb0)) + // (ObjCMessageExpr 0x231ccb0 'int' selector=rrrr + // (DeclRefExpr 0x231cc90 'Foo *' Var='obj2' 0x231cbe0))) + // + // 'newStmt' represents the rewritten message expression. For example: + // + // (CallExpr 0x231d300 'id':'struct objc_object *' + // (ParenExpr 0x231d2e0 'int (*)(id, SEL)' + // (CStyleCastExpr 0x231d2c0 'int (*)(id, SEL)' + // (CStyleCastExpr 0x231d220 'void *' + // (DeclRefExpr 0x231d200 'id (id, SEL, ...)' FunctionDecl='objc_msgSend' 0x231cdc0)))) + // + // Note that 'newStmt' is passed to RewritePropertySetter so that it + // can be used as the setter argument. ReplaceStmt() will still 'see' + // the original RHS (since we haven't altered BinOp). + // + // This implies the Rewrite* routines can no longer delete the original + // node. As a result, we now leak the original AST nodes. + // + return RewritePropertySetter(BinOp, dyn_cast<Expr>(newStmt), SrcRange); + } else { + return RewritePropertyGetter(PropRefExpr); + } + } + if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast<ObjCStringLiteral>(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCMessageExpr *MessExpr = dyn_cast<ObjCMessageExpr>(S)) { +#if 0 + // Before we rewrite it, put the original message expression in a comment. + SourceLocation startLoc = MessExpr->getLocStart(); + SourceLocation endLoc = MessExpr->getLocEnd(); + + const char *startBuf = SM->getCharacterData(startLoc); + const char *endBuf = SM->getCharacterData(endLoc); + + std::string messString; + messString += "// "; + messString.append(startBuf, endBuf-startBuf+1); + messString += "\n"; + + // FIXME: Missing definition of + // InsertText(clang::SourceLocation, char const*, unsigned int). + // InsertText(startLoc, messString.c_str(), messString.size()); + // Tried this, but it didn't work either... + // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); +#endif + return RewriteMessageExpr(MessExpr); + } + + if (ObjCAtTryStmt *StmtTry = dyn_cast<ObjCAtTryStmt>(S)) + return RewriteObjCTryStmt(StmtTry); + + if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast<ObjCAtSynchronizedStmt>(S)) + return RewriteObjCSynchronizedStmt(StmtTry); + + if (ObjCAtThrowStmt *StmtThrow = dyn_cast<ObjCAtThrowStmt>(S)) + return RewriteObjCThrowStmt(StmtThrow); + + if (ObjCProtocolExpr *ProtocolExp = dyn_cast<ObjCProtocolExpr>(S)) + return RewriteObjCProtocolExpr(ProtocolExp); + + if (ObjCForCollectionStmt *StmtForCollection = + dyn_cast<ObjCForCollectionStmt>(S)) + return RewriteObjCForCollectionStmt(StmtForCollection, + OrigStmtRange.getEnd()); + if (BreakStmt *StmtBreakStmt = + dyn_cast<BreakStmt>(S)) + return RewriteBreakStmt(StmtBreakStmt); + if (ContinueStmt *StmtContinueStmt = + dyn_cast<ContinueStmt>(S)) + return RewriteContinueStmt(StmtContinueStmt); + + // Need to check for protocol refs (id <P>, Foo <P> *) in variable decls + // and cast exprs. + if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + // FIXME: What we're doing here is modifying the type-specifier that + // precedes the first Decl. In the future the DeclGroup should have + // a separate type-specifier that we can rewrite. + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + Decl *SD = *DI; + if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + } + if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) + RewriteObjCQualifiedInterfaceTypes(CE); + + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) { + assert(!Stmts.empty() && "Statement stack is empty"); + assert ((isa<SwitchStmt>(Stmts.back()) || isa<WhileStmt>(Stmts.back()) || + isa<DoStmt>(Stmts.back()) || isa<ForStmt>(Stmts.back())) + && "Statement stack mismatch"); + Stmts.pop_back(); + } + // Handle blocks rewriting. + if (BlockDeclRefExpr *BDRE = dyn_cast<BlockDeclRefExpr>(S)) { + if (BDRE->isByRef()) + RewriteBlockDeclRefExpr(BDRE); + } + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) { + RewriteCastExpr(CE); + } +#if 0 + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { + CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), ICE->getSubExpr(), SourceLocation()); + // Get the new text. + std::string SStr; + llvm::raw_string_ostream Buf(SStr); + Replacement->printPretty(Buf); + const std::string &Str = Buf.str(); + + printf("CAST = %s\n", &Str[0]); + InsertText(ICE->getSubExpr()->getLocStart(), &Str[0], Str.size()); + delete S; + return Replacement; + } +#endif + // Return this stmt unmodified. + return S; +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteObjC::HandleDeclInMainFile(Decl *D) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isOverloadedOperator()) + return; + + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteBlocksInFunctionProtoType(FD->getType(), FD); + + if (CompoundStmt *Body = FD->getBody()) { + CurFunctionDef = FD; + CollectPropertySetters(Body); + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + CurFunctionDef = 0; + } + return; + } + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (CompoundStmt *Body = MD->getBody()) { + CurMethodDef = MD; + CollectPropertySetters(Body); + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + InsertBlockLiteralsWithinMethod(MD); + CurMethodDef = 0; + } + } + if (ObjCImplementationDecl *CI = dyn_cast<ObjCImplementationDecl>(D)) + ClassImplementation.push_back(CI); + else if (ObjCCategoryImplDecl *CI = dyn_cast<ObjCCategoryImplDecl>(D)) + CategoryImplementation.push_back(CI); + else if (ObjCClassDecl *CD = dyn_cast<ObjCClassDecl>(D)) + RewriteForwardClassDecl(CD); + else if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + RewriteObjCQualifiedInterfaceTypes(VD); + if (isTopLevelBlockPointerType(VD->getType())) + RewriteBlockPointerDecl(VD); + else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CollectPropertySetters(VD->getInit()); + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), + VD->getNameAsCString()); + GlobalVarDecl = 0; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + return; + } + if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + return; + } + if (RecordDecl *RD = dyn_cast<RecordDecl>(D)) { + if (RD->isDefinition()) { + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + FieldDecl *FD = *i; + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + } + } + return; + } + // Nothing yet. +} + +void RewriteObjC::HandleTranslationUnit(TranslationUnit& TU) { + // Get the top-level buffer that this corresponds to. + + // Rewrite tabs if we care. + //RewriteTabs(); + + if (Diags.hasErrorOccurred()) + return; + + // Create the output file. + + llvm::OwningPtr<llvm::raw_ostream> OwnedStream; + llvm::raw_ostream *OutFile; + if (OutFileName == "-") { + OutFile = &llvm::outs(); + } else if (!OutFileName.empty()) { + std::string Err; + OutFile = new llvm::raw_fd_ostream(OutFileName.c_str(), + // set binary mode (critical for Windoze) + true, + Err); + OwnedStream.reset(OutFile); + } else if (InFileName == "-") { + OutFile = &llvm::outs(); + } else { + llvm::sys::Path Path(InFileName); + Path.eraseSuffix(); + Path.appendSuffix("cpp"); + std::string Err; + OutFile = new llvm::raw_fd_ostream(Path.toString().c_str(), + // set binary mode (critical for Windoze) + true, + Err); + OwnedStream.reset(OutFile); + } + + RewriteInclude(); + + InsertText(SM->getLocForStartOfFile(MainFileID), + Preamble.c_str(), Preamble.size(), false); + + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + fprintf(stderr, "No changes\n"); + } + + if (ClassImplementation.size() || CategoryImplementation.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + SynthesizeMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + OutFile->flush(); +} + diff --git a/clang/tools/clang-cc/RewriteTest.cpp b/clang/tools/clang-cc/RewriteTest.cpp new file mode 100644 index 00000000000..1d0c6f9e9c0 --- /dev/null +++ b/clang/tools/clang-cc/RewriteTest.cpp @@ -0,0 +1,40 @@ +//===--- RewriteTest.cpp - Rewriter playground ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a testbed. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/TokenRewriter.h" +#include <iostream> + +void clang::DoRewriteTest(Preprocessor &PP, const std::string &InFileName, + const std::string &OutFileName) { + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOptions(); + + TokenRewriter Rewriter(SM.getMainFileID(), SM, LangOpts); + + // Throw <i> </i> tags around comments. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) { + if (I->isNot(tok::comment)) continue; + + Rewriter.AddTokenBefore(I, "<i>"); + Rewriter.AddTokenAfter(I, "</i>"); + } + + + // Print out the output. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) + std::cout << PP.getSpelling(*I); +} diff --git a/clang/tools/clang-cc/SerializationTest.cpp b/clang/tools/clang-cc/SerializationTest.cpp new file mode 100644 index 00000000000..e489a19ff83 --- /dev/null +++ b/clang/tools/clang-cc/SerializationTest.cpp @@ -0,0 +1,193 @@ +//===--- SerializationTest.cpp - Experimental Object Serialization --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements prototype code for serialization of objects in clang. +// It is not intended yet for public use, but simply is a placeholder to +// experiment with new serialization features. Serialization will eventually +// be integrated as a proper component of the clang libraries. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/CFG.h" +#include "clang/AST/Decl.h" +#include "clang.h" +#include "ASTConsumers.h" +#include "clang/AST/TranslationUnit.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/Streams.h" +#include <fstream> +#include <cstring> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Driver code. +//===----------------------------------------------------------------------===// + +namespace { + +class SerializationTest : public ASTConsumer { + Diagnostic &Diags; + FileManager &FMgr; +public: + SerializationTest(Diagnostic &d, FileManager& fmgr) + : Diags(d), FMgr(fmgr) {} + + ~SerializationTest() {} + + virtual void HandleTranslationUnit(TranslationUnit& TU); + +private: + bool Serialize(llvm::sys::Path& Filename, llvm::sys::Path& FNameDeclPrint, + TranslationUnit& TU); + + bool Deserialize(llvm::sys::Path& Filename, llvm::sys::Path& FNameDeclPrint); +}; + +} // end anonymous namespace + +ASTConsumer* +clang::CreateSerializationTest(Diagnostic &Diags, FileManager& FMgr) { + return new SerializationTest(Diags, FMgr); +} + + +bool SerializationTest::Serialize(llvm::sys::Path& Filename, + llvm::sys::Path& FNameDeclPrint, + TranslationUnit& TU) { + { + // Pretty-print the decls to a temp file. + std::string Err; + llvm::raw_fd_ostream DeclPP(FNameDeclPrint.c_str(), true, Err); + assert (Err.empty() && "Could not open file for printing out decls."); + llvm::OwningPtr<ASTConsumer> FilePrinter(CreateASTPrinter(&DeclPP)); + + for (TranslationUnit::iterator I=TU.begin(), E=TU.end(); I!=E; ++I) + FilePrinter->HandleTopLevelDecl(*I); + } + + // Serialize the translation unit. + return EmitASTBitcodeFile(TU,Filename); +} + +bool SerializationTest::Deserialize(llvm::sys::Path& Filename, + llvm::sys::Path& FNameDeclPrint) { + + // Deserialize the translation unit. + TranslationUnit* NewTU = ReadASTBitcodeFile(Filename, FMgr); + + if (!NewTU) + return false; + + { + // Pretty-print the deserialized decls to a temp file. + std::string Err; + llvm::raw_fd_ostream DeclPP(FNameDeclPrint.c_str(), true, Err); + assert (Err.empty() && "Could not open file for printing out decls."); + llvm::OwningPtr<ASTConsumer> FilePrinter(CreateASTPrinter(&DeclPP)); + + for (TranslationUnit::iterator I=NewTU->begin(), E=NewTU->end(); I!=E; ++I) + FilePrinter->HandleTopLevelDecl(*I); + } + + delete NewTU; + + return true; +} + +namespace { + class TmpDirJanitor { + llvm::sys::Path& Dir; + public: + explicit TmpDirJanitor(llvm::sys::Path& dir) : Dir(dir) {} + + ~TmpDirJanitor() { + llvm::cerr << "Removing: " << Dir.c_str() << '\n'; + Dir.eraseFromDisk(true); + } + }; +} + +void SerializationTest::HandleTranslationUnit(TranslationUnit& TU) { + + std::string ErrMsg; + llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); + + if (Dir.isEmpty()) { + llvm::cerr << "Error: " << ErrMsg << "\n"; + return; + } + + TmpDirJanitor RemoveTmpOnExit(Dir); + + llvm::sys::Path FNameDeclBefore = Dir; + FNameDeclBefore.appendComponent("test.decl_before.txt"); + + if (FNameDeclBefore.makeUnique(true,&ErrMsg)) { + llvm::cerr << "Error: " << ErrMsg << "\n"; + return; + } + + llvm::sys::Path FNameDeclAfter = Dir; + FNameDeclAfter.appendComponent("test.decl_after.txt"); + + if (FNameDeclAfter.makeUnique(true,&ErrMsg)) { + llvm::cerr << "Error: " << ErrMsg << "\n"; + return; + } + + llvm::sys::Path ASTFilename = Dir; + ASTFilename.appendComponent("test.ast"); + + if (ASTFilename.makeUnique(true,&ErrMsg)) { + llvm::cerr << "Error: " << ErrMsg << "\n"; + return; + } + + // Serialize and then deserialize the ASTs. + bool status = Serialize(ASTFilename, FNameDeclBefore, TU); + assert (status && "Serialization failed."); + status = Deserialize(ASTFilename, FNameDeclAfter); + assert (status && "Deserialization failed."); + + // Read both pretty-printed files and compare them. + + using llvm::MemoryBuffer; + + llvm::OwningPtr<MemoryBuffer> + MBufferSer(MemoryBuffer::getFile(FNameDeclBefore.c_str())); + + if(!MBufferSer) { + llvm::cerr << "ERROR: Cannot read pretty-printed file (pre-pickle).\n"; + return; + } + + llvm::OwningPtr<MemoryBuffer> + MBufferDSer(MemoryBuffer::getFile(FNameDeclAfter.c_str())); + + if(!MBufferDSer) { + llvm::cerr << "ERROR: Cannot read pretty-printed file (post-pickle).\n"; + return; + } + + const char *p1 = MBufferSer->getBufferStart(); + const char *e1 = MBufferSer->getBufferEnd(); + const char *p2 = MBufferDSer->getBufferStart(); + const char *e2 = MBufferDSer->getBufferEnd(); + + if (MBufferSer->getBufferSize() == MBufferDSer->getBufferSize()) + for ( ; p1 != e1 ; ++p1, ++p2 ) + if (*p1 != *p2) break; + + if (p1 != e1 || p2 != e2 ) + llvm::cerr << "ERROR: Pretty-printed files are not the same.\n"; + else + llvm::cerr << "SUCCESS: Pretty-printed files are the same.\n"; +} diff --git a/clang/tools/clang-cc/Warnings.cpp b/clang/tools/clang-cc/Warnings.cpp new file mode 100644 index 00000000000..18e44d72e3c --- /dev/null +++ b/clang/tools/clang-cc/Warnings.cpp @@ -0,0 +1,222 @@ +//===--- Warnings.cpp - C-Language Front-end ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Command line warning options handler. +// +//===----------------------------------------------------------------------===// +// +// This file is responsible for handling all warning options. This includes +// a number of -Wfoo options and their variants, which are driven by TableGen- +// generated data, and the special cases -pedantic, -pedantic-errors, -w and +// -Werror. +// +// Warning options control the handling of the warnings that Clang emits. There +// are three possible reactions to any given warning: +// ignore: Do nothing +// warn: Emit a message, but don't fail the compilation +// error: Emit a message and fail the compilation +// +// Each warning option controls any number of actual warnings. +// Given a warning option 'foo', the following are valid: +// -Wfoo=ignore -> Ignore the controlled warnings. +// -Wfoo=warn -> Warn about the controlled warnings. +// -Wfoo=error -> Fail on the controlled warnings. +// -Wfoo -> alias of -Wfoo=warn +// -Wno-foo -> alias of -Wfoo=ignore +// -Werror=foo -> alias of -Wfoo=error +// +// Because of this complex handling of options, the default parser is replaced. + +#include "clang/Basic/Diagnostic.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Lex/LexDiagnostic.h" +#include "llvm/Support/CommandLine.h" +#include <vector> +#include <string> +#include <utility> +#include <algorithm> +#include <string.h> + +using namespace clang; + +namespace { + struct ParsedOption { + std::string Name; + diag::Mapping Mapping; + + ParsedOption() {} + // Used by -Werror, implicitly. + ParsedOption(const std::string& name) : Name(name), Mapping(diag::MAP_ERROR) + {} + }; + + typedef std::vector<ParsedOption> OptionsList; + + OptionsList Options; + + struct WarningParser : public llvm::cl::basic_parser<ParsedOption> { + diag::Mapping StrToMapping(const std::string &S) { + if (S == "ignore") + return diag::MAP_IGNORE; + if (S == "warn") + return diag::MAP_WARNING; + if (S == "error") + return diag::MAP_ERROR; + return diag::MAP_DEFAULT; + } + bool parse(llvm::cl::Option &O, const char *ArgName, + const std::string &ArgValue, ParsedOption &Val) + { + size_t Eq = ArgValue.find("="); + if (Eq == std::string::npos) { + // Could be -Wfoo or -Wno-foo + if (ArgValue.compare(0, 3, "no-") == 0) { + Val.Name = ArgValue.substr(3); + Val.Mapping = diag::MAP_IGNORE; + } else { + Val.Name = ArgValue; + Val.Mapping = diag::MAP_WARNING; + } + } else { + Val.Name = ArgValue.substr(0, Eq); + Val.Mapping = StrToMapping(ArgValue.substr(Eq+1)); + if (Val.Mapping == diag::MAP_DEFAULT) { + fprintf(stderr, "Illegal warning option value: %s\n", + ArgValue.substr(Eq+1).c_str()); + return true; + } + } + return false; + } + }; +} + +static llvm::cl::list<ParsedOption, OptionsList, WarningParser> +OptWarnings("W", llvm::cl::location(Options), llvm::cl::Prefix); + +static llvm::cl::list<ParsedOption, OptionsList, llvm::cl::parser<std::string> > +OptWError("Werror", llvm::cl::location(Options), llvm::cl::CommaSeparated, + llvm::cl::ValueOptional); + +static llvm::cl::opt<bool> OptPedantic("pedantic"); +static llvm::cl::opt<bool> OptPedanticErrors("pedantic-errors"); +static llvm::cl::opt<bool> OptNoWarnings("w"); +static llvm::cl::opt<bool> +OptWarnInSystemHeaders("Wsystem-headers", + llvm::cl::desc("Do not suppress warnings issued in system headers")); + +namespace { + struct WarningOption { + const char *Name; + const diag::kind *Members; + size_t NumMembers; + }; + bool operator <(const WarningOption& lhs, const WarningOption& rhs) { + return strcmp(lhs.Name, rhs.Name) < 0; + } +} +#define DIAGS(a) a, (sizeof(a) / sizeof(a[0])) +// These tables will be TableGenerated later. +// First the table sets describing the diagnostics controlled by each option. +static const diag::kind UnusedMacrosDiags[] = { diag::pp_macro_not_used }; +static const diag::kind FloatEqualDiags[] = { diag::warn_floatingpoint_eq }; +static const diag::kind ReadOnlySetterAttrsDiags[] = { + diag::warn_objc_property_attr_mutually_exclusive +}; +static const diag::kind FormatNonLiteralDiags[] = { + diag::warn_printf_not_string_constant +}; +static const diag::kind UndefDiags[] = { diag::warn_pp_undef_identifier }; +static const diag::kind ImplicitFunctionDeclarationDiags[] = { + diag::ext_implicit_function_decl, diag::warn_implicit_function_decl +}; +// Hmm ... this option is currently actually completely ignored. +//static const diag::kind StrictSelectorMatchDiags[] = { }; +// Second the table of options. MUST be sorted by name! Binary lookup is done. +static const WarningOption OptionTable[] = { + { "float-equal", DIAGS(FloatEqualDiags) }, + { "format-nonliteral", DIAGS(FormatNonLiteralDiags) }, + { "implicit-function-declaration", DIAGS(ImplicitFunctionDeclarationDiags) }, + { "readonly-setter-attrs", DIAGS(ReadOnlySetterAttrsDiags) }, + { "undef", DIAGS(UndefDiags) }, + { "unused-macros", DIAGS(UnusedMacrosDiags) }, +// { "strict-selector-match", DIAGS(StrictSelectorMatchDiags) } +}; +static const size_t OptionTableSize = + sizeof(OptionTable) / sizeof(OptionTable[0]); + +namespace clang { + +bool ProcessWarningOptions(Diagnostic &Diags) { + // FIXME: These should be mapped to group options. + Diags.setIgnoreAllWarnings(OptNoWarnings); + Diags.setWarnOnExtensions(OptPedantic); + Diags.setErrorOnExtensions(OptPedanticErrors); + + // Set some defaults that are currently set manually. This, too, should + // be in the tablegen stuff later. + Diags.setDiagnosticMapping(diag::pp_macro_not_used, diag::MAP_IGNORE); + Diags.setDiagnosticMapping(diag::warn_floatingpoint_eq, diag::MAP_IGNORE); + Diags.setDiagnosticMapping(diag::warn_objc_property_attr_mutually_exclusive, + diag::MAP_IGNORE); + Diags.setDiagnosticMapping(diag::warn_pp_undef_identifier, diag::MAP_IGNORE); + Diags.setDiagnosticMapping(diag::warn_implicit_function_decl, + diag::MAP_IGNORE); + + Diags.setDiagnosticMapping(diag::err_pp_file_not_found, diag::MAP_FATAL); + Diags.setDiagnosticMapping(diag::err_template_recursion_depth_exceeded, + diag::MAP_FATAL); + Diags.setSuppressSystemWarnings(!OptWarnInSystemHeaders); + + for (OptionsList::iterator it = Options.begin(), e = Options.end(); + it != e; ++it) { + if (it->Name.empty()) { + // Empty string is "everything". This way, -Werror does the right thing. + // FIXME: These flags do not participate in proper option overriding. + switch(it->Mapping) { + default: + assert(false && "Illegal mapping"); + break; + + case diag::MAP_IGNORE: + Diags.setIgnoreAllWarnings(true); + Diags.setWarningsAsErrors(false); + break; + + case diag::MAP_WARNING: + Diags.setIgnoreAllWarnings(false); + Diags.setWarningsAsErrors(false); + break; + + case diag::MAP_ERROR: + Diags.setIgnoreAllWarnings(false); + Diags.setWarningsAsErrors(true); + break; + } + continue; + } + WarningOption Key = { it->Name.c_str(), 0, 0 }; + const WarningOption *Found = std::lower_bound(OptionTable, + OptionTable + OptionTableSize, + Key); + if (Found == OptionTable + OptionTableSize || + strcmp(Found->Name, Key.Name) != 0) { + fprintf(stderr, "Unknown warning option: -W%s\n", Key.Name); + return true; + } + + // Option exists. + for (size_t i = 0; i < Found->NumMembers; ++i) { + Diags.setDiagnosticMapping(Found->Members[i], it->Mapping); + } + } + return false; +} + +} diff --git a/clang/tools/clang-cc/clang.cpp b/clang/tools/clang-cc/clang.cpp new file mode 100644 index 00000000000..2ae1b6ec737 --- /dev/null +++ b/clang/tools/clang-cc/clang.cpp @@ -0,0 +1,1703 @@ +//===--- clang.cpp - C-Language Front-end ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility may be invoked in the following manner: +// clang --help - Output help info. +// clang [options] - Read from stdin. +// clang [options] file - Read from "file". +// clang [options] file1 file2 - Read these files. +// +//===----------------------------------------------------------------------===// +// +// TODO: Options to support: +// +// -Wfatal-errors +// -ftabstop=width +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "ASTConsumers.h" +#include "clang/Frontend/CompileOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/InitHeaderSearch.h" +#include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/AST/Decl.h" +#include "clang/AST/TranslationUnit.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Sema/ParseAST.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Parse/Parser.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Timer.h" +#include "llvm/System/Host.h" +#include "llvm/System/Path.h" +#include "llvm/System/Signals.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// Global options. +//===----------------------------------------------------------------------===// + +/// ClangFrontendTimer - The front-end activities should charge time to it with +/// TimeRegion. The -ftime-report option controls whether this will do +/// anything. +llvm::Timer *ClangFrontendTimer = 0; + +static bool HadErrors = false; + +static llvm::cl::opt<bool> +Verbose("v", llvm::cl::desc("Enable verbose output")); +static llvm::cl::opt<bool> +Stats("print-stats", + llvm::cl::desc("Print performance metrics and statistics")); +static llvm::cl::opt<bool> +DisableFree("disable-free", + llvm::cl::desc("Disable freeing of memory on exit"), + llvm::cl::init(false)); + +enum ProgActions { + RewriteObjC, // ObjC->C Rewriter. + RewriteBlocks, // ObjC->C Rewriter for Blocks. + RewriteMacros, // Expand macros but not #includes. + RewriteTest, // Rewriter playground + HTMLTest, // HTML displayer testing stuff. + EmitAssembly, // Emit a .s file. + EmitLLVM, // Emit a .ll file. + EmitBC, // Emit a .bc file. + EmitLLVMOnly, // Generate LLVM IR, but do not + SerializeAST, // Emit a .ast file. + EmitHTML, // Translate input source into HTML. + ASTPrint, // Parse ASTs and print them. + ASTDump, // Parse ASTs and dump them. + ASTView, // Parse ASTs and view them in Graphviz. + PrintDeclContext, // Print DeclContext and their Decls. + TestSerialization, // Run experimental serialization code. + ParsePrintCallbacks, // Parse and print each callback. + ParseSyntaxOnly, // Parse and perform semantic analysis. + ParseNoop, // Parse with noop callbacks. + RunPreprocessorOnly, // Just lex, no output. + PrintPreprocessedInput, // -E mode. + DumpTokens, // Dump out preprocessed tokens. + DumpRawTokens, // Dump out raw tokens. + RunAnalysis, // Run one or more source code analyses. + GeneratePCH, // Generate precompiled header. + InheritanceView // View C++ inheritance for a specified class. +}; + +static llvm::cl::opt<ProgActions> +ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, + llvm::cl::init(ParseSyntaxOnly), + llvm::cl::values( + clEnumValN(RunPreprocessorOnly, "Eonly", + "Just run preprocessor, no output (for timings)"), + clEnumValN(PrintPreprocessedInput, "E", + "Run preprocessor, emit preprocessed file"), + clEnumValN(DumpRawTokens, "dump-raw-tokens", + "Lex file in raw mode and dump raw tokens"), + clEnumValN(RunAnalysis, "analyze", + "Run static analysis engine"), + clEnumValN(DumpTokens, "dump-tokens", + "Run preprocessor, dump internal rep of tokens"), + clEnumValN(ParseNoop, "parse-noop", + "Run parser with noop callbacks (for timings)"), + clEnumValN(ParseSyntaxOnly, "fsyntax-only", + "Run parser and perform semantic analysis"), + clEnumValN(ParsePrintCallbacks, "parse-print-callbacks", + "Run parser and print each callback invoked"), + clEnumValN(EmitHTML, "emit-html", + "Output input source as HTML"), + clEnumValN(ASTPrint, "ast-print", + "Build ASTs and then pretty-print them"), + clEnumValN(ASTDump, "ast-dump", + "Build ASTs and then debug dump them"), + clEnumValN(ASTView, "ast-view", + "Build ASTs and view them with GraphViz"), + clEnumValN(PrintDeclContext, "print-decl-contexts", + "Print DeclContexts and their Decls."), + clEnumValN(TestSerialization, "test-pickling", + "Run prototype serialization code"), + clEnumValN(EmitAssembly, "S", + "Emit native assembly code"), + clEnumValN(EmitLLVM, "emit-llvm", + "Build ASTs then convert to LLVM, emit .ll file"), + clEnumValN(EmitBC, "emit-llvm-bc", + "Build ASTs then convert to LLVM, emit .bc file"), + clEnumValN(EmitLLVMOnly, "emit-llvm-only", + "Build ASTs and convert to LLVM, discarding output"), + clEnumValN(SerializeAST, "serialize", + "Build ASTs and emit .ast file"), + clEnumValN(RewriteTest, "rewrite-test", + "Rewriter playground"), + clEnumValN(RewriteObjC, "rewrite-objc", + "Rewrite ObjC into C (code rewriter example)"), + clEnumValN(RewriteMacros, "rewrite-macros", + "Expand macros without full preprocessing"), + clEnumValN(RewriteBlocks, "rewrite-blocks", + "Rewrite Blocks to C"), + clEnumValEnd)); + + +static llvm::cl::opt<std::string> +OutputFile("o", + llvm::cl::value_desc("path"), + llvm::cl::desc("Specify output file (for --serialize, this is a directory)")); + + +//===----------------------------------------------------------------------===// +// PTH. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +TokenCache("token-cache", llvm::cl::value_desc("path"), + llvm::cl::desc("Use specified token cache file")); + +//===----------------------------------------------------------------------===// +// Diagnostic Options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +VerifyDiagnostics("verify", + llvm::cl::desc("Verify emitted diagnostics and warnings")); + +static llvm::cl::opt<std::string> +HTMLDiag("html-diags", + llvm::cl::desc("Generate HTML to report diagnostics"), + llvm::cl::value_desc("HTML directory")); + +static llvm::cl::opt<bool> +NoShowColumn("fno-show-column", + llvm::cl::desc("Do not include column number on diagnostics")); + +static llvm::cl::opt<bool> +NoShowLocation("fno-show-source-location", + llvm::cl::desc("Do not include source location information with" + " diagnostics")); + +static llvm::cl::opt<bool> +NoCaretDiagnostics("fno-caret-diagnostics", + llvm::cl::desc("Do not include source line and caret with" + " diagnostics")); + +static llvm::cl::opt<bool> +PrintSourceRangeInfo("fprint-source-range-info", + llvm::cl::desc("Print source range spans in numeric form")); + + +//===----------------------------------------------------------------------===// +// C++ Visualization. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +InheritanceViewCls("cxx-inheritance-view", + llvm::cl::value_desc("class name"), + llvm::cl::desc("View C++ inheritance for a specified class")); + +//===----------------------------------------------------------------------===// +// Builtin Options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +TimeReport("ftime-report", + llvm::cl::desc("Print the amount of time each " + "phase of compilation takes")); + +static llvm::cl::opt<bool> +Freestanding("ffreestanding", + llvm::cl::desc("Assert that the compilation takes place in a " + "freestanding environment")); + +static llvm::cl::opt<bool> +AllowBuiltins("fbuiltin", + llvm::cl::desc("Disable implicit builtin knowledge of functions"), + llvm::cl::init(true), llvm::cl::AllowInverse); + + +static llvm::cl::opt<bool> +MathErrno("fmath-errno", + llvm::cl::desc("Require math functions to respect errno"), + llvm::cl::init(true), llvm::cl::AllowInverse); + +//===----------------------------------------------------------------------===// +// Language Options +//===----------------------------------------------------------------------===// + +enum LangKind { + langkind_unspecified, + langkind_c, + langkind_c_cpp, + langkind_asm_cpp, + langkind_c_pch, + langkind_cxx, + langkind_cxx_cpp, + langkind_cxx_pch, + langkind_objc, + langkind_objc_cpp, + langkind_objc_pch, + langkind_objcxx, + langkind_objcxx_cpp, + langkind_objcxx_pch +}; + +static llvm::cl::opt<LangKind> +BaseLang("x", llvm::cl::desc("Base language to compile"), + llvm::cl::init(langkind_unspecified), + llvm::cl::values(clEnumValN(langkind_c, "c", "C"), + clEnumValN(langkind_cxx, "c++", "C++"), + clEnumValN(langkind_objc, "objective-c", "Objective C"), + clEnumValN(langkind_objcxx,"objective-c++","Objective C++"), + clEnumValN(langkind_c_cpp, "cpp-output", + "Preprocessed C"), + clEnumValN(langkind_asm_cpp, "assembler-with-cpp", + "Preprocessed asm"), + clEnumValN(langkind_cxx_cpp, "c++-cpp-output", + "Preprocessed C++"), + clEnumValN(langkind_objc_cpp, "objective-c-cpp-output", + "Preprocessed Objective C"), + clEnumValN(langkind_objcxx_cpp, "objective-c++-cpp-output", + "Preprocessed Objective C++"), + clEnumValN(langkind_c_pch, "c-header", + "Precompiled C header"), + clEnumValN(langkind_objc_pch, "objective-c-header", + "Precompiled Objective-C header"), + clEnumValN(langkind_cxx_pch, "c++-header", + "Precompiled C++ header"), + clEnumValN(langkind_objcxx_pch, "objective-c++-header", + "Precompiled Objective-C++ header"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +LangObjC("ObjC", llvm::cl::desc("Set base language to Objective-C"), + llvm::cl::Hidden); +static llvm::cl::opt<bool> +LangObjCXX("ObjC++", llvm::cl::desc("Set base language to Objective-C++"), + llvm::cl::Hidden); + +/// InitializeBaseLanguage - Handle the -x foo options. +static void InitializeBaseLanguage() { + if (LangObjC) + BaseLang = langkind_objc; + else if (LangObjCXX) + BaseLang = langkind_objcxx; +} + +static LangKind GetLanguage(const std::string &Filename) { + if (BaseLang != langkind_unspecified) + return BaseLang; + + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + BaseLang = langkind_c; // Default to C if no extension. + return langkind_c; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + // assembler no preprocessing: .s + // assembler: .S + if (Ext == "c") + return langkind_c; + else if (Ext == "S" || + // If the compiler is run on a .s file, preprocess it as .S + Ext == "s") + return langkind_asm_cpp; + else if (Ext == "i") + return langkind_c_cpp; + else if (Ext == "ii") + return langkind_cxx_cpp; + else if (Ext == "m") + return langkind_objc; + else if (Ext == "mi") + return langkind_objc_cpp; + else if (Ext == "mm" || Ext == "M") + return langkind_objcxx; + else if (Ext == "mii") + return langkind_objcxx_cpp; + else if (Ext == "C" || Ext == "cc" || Ext == "cpp" || Ext == "CPP" || + Ext == "c++" || Ext == "cp" || Ext == "cxx") + return langkind_cxx; + else + return langkind_c; +} + + +static void InitializeCOptions(LangOptions &Options) { + // Do nothing. +} + +static void InitializeObjCOptions(LangOptions &Options) { + Options.ObjC1 = Options.ObjC2 = 1; +} + + +static bool InitializeLangOptions(LangOptions &Options, LangKind LK){ + // FIXME: implement -fpreprocessed mode. + bool NoPreprocess = false; + bool PCH = false; + + // Test for 'PCH'. + switch (LK) { + default: + break; + case langkind_c_pch: + LK = langkind_c; + PCH = true; + break; + case langkind_objc_pch: + LK = langkind_objc; + PCH = true; + break; + case langkind_cxx_pch: + LK = langkind_cxx; + PCH = true; + break; + case langkind_objcxx_pch: + LK = langkind_objcxx; + PCH = true; + break; + } + + switch (LK) { + default: assert(0 && "Unknown language kind!"); + case langkind_asm_cpp: + Options.AsmPreprocessor = 1; + // FALLTHROUGH + case langkind_c_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_c: + InitializeCOptions(Options); + break; + case langkind_cxx_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_cxx: + Options.CPlusPlus = 1; + break; + case langkind_objc_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_objc: + InitializeObjCOptions(Options); + break; + case langkind_objcxx_cpp: + NoPreprocess = true; + // FALLTHROUGH + case langkind_objcxx: + Options.ObjC1 = Options.ObjC2 = 1; + Options.CPlusPlus = 1; + break; + } + + return PCH; +} + +/// LangStds - Language standards we support. +enum LangStds { + lang_unspecified, + lang_c89, lang_c94, lang_c99, + lang_gnu_START, + lang_gnu89 = lang_gnu_START, lang_gnu99, + lang_cxx98, lang_gnucxx98, + lang_cxx0x, lang_gnucxx0x +}; + +static llvm::cl::opt<LangStds> +LangStd("std", llvm::cl::desc("Language standard to compile for"), + llvm::cl::init(lang_unspecified), + llvm::cl::values(clEnumValN(lang_c89, "c89", "ISO C 1990"), + clEnumValN(lang_c89, "c90", "ISO C 1990"), + clEnumValN(lang_c89, "iso9899:1990", "ISO C 1990"), + clEnumValN(lang_c94, "iso9899:199409", + "ISO C 1990 with amendment 1"), + clEnumValN(lang_c99, "c99", "ISO C 1999"), +// clEnumValN(lang_c99, "c9x", "ISO C 1999"), + clEnumValN(lang_c99, "iso9899:1999", "ISO C 1999"), +// clEnumValN(lang_c99, "iso9899:199x", "ISO C 1999"), + clEnumValN(lang_gnu89, "gnu89", + "ISO C 1990 with GNU extensions"), + clEnumValN(lang_gnu99, "gnu99", + "ISO C 1999 with GNU extensions (default for C)"), + clEnumValN(lang_gnu99, "gnu9x", + "ISO C 1999 with GNU extensions"), + clEnumValN(lang_cxx98, "c++98", + "ISO C++ 1998 with amendments"), + clEnumValN(lang_gnucxx98, "gnu++98", + "ISO C++ 1998 with amendments and GNU " + "extensions (default for C++)"), + clEnumValN(lang_cxx0x, "c++0x", + "Upcoming ISO C++ 200x with amendments"), + clEnumValN(lang_gnucxx0x, "gnu++0x", + "Upcoming ISO C++ 200x with amendments and GNU " + "extensions"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +NoOperatorNames("fno-operator-names", + llvm::cl::desc("Do not treat C++ operator name keywords as " + "synonyms for operators")); + +static llvm::cl::opt<bool> +PascalStrings("fpascal-strings", + llvm::cl::desc("Recognize and construct Pascal-style " + "string literals")); + +static llvm::cl::opt<bool> +MSExtensions("fms-extensions", + llvm::cl::desc("Accept some non-standard constructs used in " + "Microsoft header files ")); + +static llvm::cl::opt<bool> +WritableStrings("fwritable-strings", + llvm::cl::desc("Store string literals as writable data")); + +static llvm::cl::opt<bool> +NoLaxVectorConversions("fno-lax-vector-conversions", + llvm::cl::desc("Disallow implicit conversions between " + "vectors with a different number of " + "elements or different element types")); + +static llvm::cl::opt<bool> +EnableBlocks("fblocks", llvm::cl::desc("enable the 'blocks' language feature"), + llvm::cl::ValueDisallowed, llvm::cl::AllowInverse, + llvm::cl::ZeroOrMore); + +static llvm::cl::opt<bool> +EnableHeinousExtensions("fheinous-gnu-extensions", + llvm::cl::desc("enable GNU extensions that you really really shouldn't use"), + llvm::cl::ValueDisallowed, llvm::cl::Hidden); + +static llvm::cl::opt<bool> +ObjCNonFragileABI("fobjc-nonfragile-abi", + llvm::cl::desc("enable objective-c's nonfragile abi")); + +static llvm::cl::opt<bool> +EmitAllDecls("femit-all-decls", + llvm::cl::desc("Emit all declarations, even if unused")); + +// FIXME: This (and all GCC -f options) really come in -f... and +// -fno-... forms, and additionally support automagic behavior when +// they are not defined. For example, -fexceptions defaults to on or +// off depending on the language. We should support this behavior in +// some form (perhaps just add a facility for distinguishing when an +// has its default value from when it has been set to its default +// value). +static llvm::cl::opt<bool> +Exceptions("fexceptions", + llvm::cl::desc("Enable support for exception handling.")); + +static llvm::cl::opt<bool> +GNURuntime("fgnu-runtime", + llvm::cl::desc("Generate output compatible with the standard GNU " + "Objective-C runtime.")); + +static llvm::cl::opt<bool> +NeXTRuntime("fnext-runtime", + llvm::cl::desc("Generate output compatible with the NeXT " + "runtime.")); + + + +static llvm::cl::opt<bool> +Trigraphs("trigraphs", llvm::cl::desc("Process trigraph sequences.")); + +static llvm::cl::opt<bool> +Ansi("ansi", llvm::cl::desc("Equivalent to specifying -std=c89.")); + +static llvm::cl::list<std::string> +TargetFeatures("mattr", llvm::cl::CommaSeparated, + llvm::cl::desc("Target specific attributes (-mattr=help for details)")); + +static llvm::cl::opt<unsigned> +TemplateDepth("ftemplate-depth", llvm::cl::init(99), + llvm::cl::desc("Maximum depth of recursive template " + "instantiation")); + +// FIXME: add: +// -fdollars-in-identifiers +static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, + TargetInfo *Target) { + // Allow the target to set the default the langauge options as it sees fit. + Target->getDefaultLangOptions(Options); + + // If there are any -mattr options, pass them to the target for validation and + // processing. The driver should have already consolidated all the + // target-feature settings and passed them to us in the -mattr list. The + // -mattr list is treated by the code generator as a diff against the -mcpu + // setting, but the driver should pass all enabled options as "+" settings. + // This means that the target should only look at + settings. + if (!TargetFeatures.empty() + // FIXME: The driver is not quite yet ready for this. + && 0) { + std::string ErrorStr; + int Opt = Target->HandleTargetFeatures(&TargetFeatures[0], + TargetFeatures.size(), ErrorStr); + if (Opt != -1) { + if (ErrorStr.empty()) + fprintf(stderr, "invalid feature '%s'\n", + TargetFeatures[Opt].c_str()); + else + fprintf(stderr, "feature '%s': %s\n", + TargetFeatures[Opt].c_str(), ErrorStr.c_str()); + exit(1); + } + } + + if (Ansi) // "The -ansi option is equivalent to -std=c89." + LangStd = lang_c89; + + if (LangStd == lang_unspecified) { + // Based on the base language, pick one. + switch (LK) { + case lang_unspecified: assert(0 && "Unknown base language"); + case langkind_c: + case langkind_asm_cpp: + case langkind_c_cpp: + case langkind_c_pch: + case langkind_objc: + case langkind_objc_cpp: + case langkind_objc_pch: + LangStd = lang_gnu99; + break; + case langkind_cxx: + case langkind_cxx_cpp: + case langkind_cxx_pch: + case langkind_objcxx: + case langkind_objcxx_cpp: + case langkind_objcxx_pch: + LangStd = lang_gnucxx98; + break; + } + } + + switch (LangStd) { + default: assert(0 && "Unknown language standard!"); + + // Fall through from newer standards to older ones. This isn't really right. + // FIXME: Enable specifically the right features based on the language stds. + case lang_gnucxx0x: + case lang_cxx0x: + Options.CPlusPlus0x = 1; + // FALL THROUGH + case lang_gnucxx98: + case lang_cxx98: + Options.CPlusPlus = 1; + Options.CXXOperatorNames = !NoOperatorNames; + Options.Boolean = 1; + // FALL THROUGH. + case lang_gnu99: + case lang_c99: + Options.C99 = 1; + Options.HexFloats = 1; + // FALL THROUGH. + case lang_gnu89: + Options.BCPLComment = 1; // Only for C99/C++. + // FALL THROUGH. + case lang_c94: + Options.Digraphs = 1; // C94, C99, C++. + // FALL THROUGH. + case lang_c89: + break; + } + + // GNUMode - Set if we're in gnu99, gnu89, gnucxx98, etc. + Options.GNUMode = LangStd >= lang_gnu_START; + + if (Options.CPlusPlus) { + Options.C99 = 0; + Options.HexFloats = Options.GNUMode; + } + + if (LangStd == lang_c89 || LangStd == lang_c94 || LangStd == lang_gnu89) + Options.ImplicitInt = 1; + else + Options.ImplicitInt = 0; + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs or -ansi + // is specified, or -std is set to a conforming mode. + Options.Trigraphs = !Options.GNUMode; + if (Trigraphs.getPosition()) + Options.Trigraphs = Trigraphs; // Command line option wins if specified. + + // If in a conformant language mode (e.g. -std=c99) Blocks defaults to off + // even if they are normally on for the target. In GNU modes (e.g. + // -std=gnu99) the default for blocks depends on the target settings. + // However, blocks are not turned off when compiling Obj-C or Obj-C++ code. + if (!Options.ObjC1 && !Options.GNUMode) + Options.Blocks = 0; + + // Never accept '$' in identifiers when preprocessing assembler. + if (LK != langkind_asm_cpp) + Options.DollarIdents = 1; // FIXME: Really a target property. + if (PascalStrings.getPosition()) + Options.PascalStrings = PascalStrings; + Options.Microsoft = MSExtensions; + Options.WritableStrings = WritableStrings; + if (NoLaxVectorConversions.getPosition()) + Options.LaxVectorConversions = 0; + Options.Exceptions = Exceptions; + if (EnableBlocks.getPosition()) + Options.Blocks = EnableBlocks; + + if (!AllowBuiltins) + Options.NoBuiltin = 1; + if (Freestanding) + Options.Freestanding = Options.NoBuiltin = 1; + + if (EnableHeinousExtensions) + Options.HeinousExtensions = 1; + + Options.MathErrno = MathErrno; + + Options.InstantiationDepth = TemplateDepth; + + // Override the default runtime if the user requested it. + if (NeXTRuntime) + Options.NeXTRuntime = 1; + else if (GNURuntime) + Options.NeXTRuntime = 0; + + if (ObjCNonFragileABI) + Options.ObjCNonFragileABI = 1; + + if (EmitAllDecls) + Options.EmitAllDecls = 1; +} + +static llvm::cl::opt<bool> +ObjCExclusiveGC("fobjc-gc-only", + llvm::cl::desc("Use GC exclusively for Objective-C related " + "memory management")); + +static llvm::cl::opt<bool> +ObjCEnableGC("fobjc-gc", + llvm::cl::desc("Enable Objective-C garbage collection")); + +void InitializeGCMode(LangOptions &Options) { + if (ObjCExclusiveGC) + Options.setGCMode(LangOptions::GCOnly); + else if (ObjCEnableGC) + Options.setGCMode(LangOptions::HybridGC); +} + +//===----------------------------------------------------------------------===// +// Target Triple Processing. +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<std::string> +TargetTriple("triple", + llvm::cl::desc("Specify target triple (e.g. i686-apple-darwin9)")); + +static llvm::cl::opt<std::string> +Arch("arch", llvm::cl::desc("Specify target architecture (e.g. i686)")); + +static llvm::cl::opt<std::string> +MacOSVersionMin("mmacosx-version-min", + llvm::cl::desc("Specify target Mac OS/X version (e.g. 10.5)")); + +// If -mmacosx-version-min=10.3.9 is specified, change the triple from being +// something like powerpc-apple-darwin9 to powerpc-apple-darwin7 +static void HandleMacOSVersionMin(std::string &Triple) { + std::string::size_type DarwinDashIdx = Triple.find("-darwin"); + if (DarwinDashIdx == std::string::npos) { + fprintf(stderr, + "-mmacosx-version-min only valid for darwin (Mac OS/X) targets\n"); + exit(1); + } + unsigned DarwinNumIdx = DarwinDashIdx + strlen("-darwin"); + + // Remove the number. + Triple.resize(DarwinNumIdx); + + // Validate that MacOSVersionMin is a 'version number', starting with 10.[3-9] + bool MacOSVersionMinIsInvalid = false; + int VersionNum = 0; + if (MacOSVersionMin.size() < 4 || + MacOSVersionMin.substr(0, 3) != "10." || + !isdigit(MacOSVersionMin[3])) { + MacOSVersionMinIsInvalid = true; + } else { + const char *Start = MacOSVersionMin.c_str()+3; + char *End = 0; + VersionNum = (int)strtol(Start, &End, 10); + + // The version number must be in the range 0-9. + MacOSVersionMinIsInvalid = (unsigned)VersionNum > 9; + + // Turn MacOSVersionMin into a darwin number: e.g. 10.3.9 is 3 -> 7. + Triple += llvm::itostr(VersionNum+4); + + if (End[0] == '.' && isdigit(End[1]) && End[2] == '\0') { // 10.4.7 is ok. + // Add the period piece (.7) to the end of the triple. This gives us + // something like ...-darwin8.7 + Triple += End; + } else if (End[0] != '\0') { // "10.4" is ok. 10.4x is not. + MacOSVersionMinIsInvalid = true; + } + } + + if (MacOSVersionMinIsInvalid) { + fprintf(stderr, + "-mmacosx-version-min=%s is invalid, expected something like '10.4'.\n", + MacOSVersionMin.c_str()); + exit(1); + } +} + +/// CreateTargetTriple - Process the various options that affect the target +/// triple and build a final aggregate triple that we are compiling for. +static std::string CreateTargetTriple() { + // Initialize base triple. If a -triple option has been specified, use + // that triple. Otherwise, default to the host triple. + std::string Triple = TargetTriple; + if (Triple.empty()) { + Triple = LLVM_HOSTTRIPLE; + + // Force i<N>86 to i386 when using LLVM_HOSTTRIPLE. + if (Triple[0] == 'i' && isdigit(Triple[1]) && + Triple[2] == '8' && Triple[3] == '6') + Triple[1] = '3'; + + // On darwin, we want to update the version to match that of the + // host. + std::string::size_type DarwinDashIdx = Triple.find("-darwin"); + if (DarwinDashIdx != std::string::npos) { + Triple.resize(DarwinDashIdx + strlen("-darwin")); + + // Only add the major part of the os version. + std::string Version = llvm::sys::getOSVersion(); + Triple += Version.substr(0, Version.find('.')); + } + } + + // If -arch foo was specified, remove the architecture from the triple we have + // so far and replace it with the specified one. + if (!Arch.empty()) { + // Decompose the base triple into "arch" and suffix. + std::string::size_type FirstDashIdx = Triple.find('-'); + + if (FirstDashIdx == std::string::npos) { + fprintf(stderr, + "Malformed target triple: \"%s\" ('-' could not be found).\n", + Triple.c_str()); + exit(1); + } + + Triple = Arch + std::string(Triple.begin()+FirstDashIdx, Triple.end()); + } + + // If -mmacosx-version-min=10.3.9 is specified, change the triple from being + // something like powerpc-apple-darwin9 to powerpc-apple-darwin7 + if (!MacOSVersionMin.empty()) + HandleMacOSVersionMin(Triple); + + return Triple; +} + +//===----------------------------------------------------------------------===// +// Preprocessor Initialization +//===----------------------------------------------------------------------===// + +// FIXME: Preprocessor builtins to support. +// -A... - Play with #assertions +// -undef - Undefine all predefined macros + +// FIXME: -imacros + +static llvm::cl::list<std::string> +D_macros("D", llvm::cl::value_desc("macro"), llvm::cl::Prefix, + llvm::cl::desc("Predefine the specified macro")); +static llvm::cl::list<std::string> +U_macros("U", llvm::cl::value_desc("macro"), llvm::cl::Prefix, + llvm::cl::desc("Undefine the specified macro")); + +static llvm::cl::list<std::string> +ImplicitIncludes("include", llvm::cl::value_desc("file"), + llvm::cl::desc("Include file before parsing")); + +static llvm::cl::opt<std::string> +ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), + llvm::cl::desc("Include file before parsing")); + +// Append a #define line to Buf for Macro. Macro should be of the form XXX, +// in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit +// "#define XXX Y z W". To get a #define with no value, use "XXX=". +static void DefineBuiltinMacro(std::vector<char> &Buf, const char *Macro, + const char *Command = "#define ") { + Buf.insert(Buf.end(), Command, Command+strlen(Command)); + if (const char *Equal = strchr(Macro, '=')) { + // Turn the = into ' '. + Buf.insert(Buf.end(), Macro, Equal); + Buf.push_back(' '); + Buf.insert(Buf.end(), Equal+1, Equal+strlen(Equal)); + } else { + // Push "macroname 1". + Buf.insert(Buf.end(), Macro, Macro+strlen(Macro)); + Buf.push_back(' '); + Buf.push_back('1'); + } + Buf.push_back('\n'); +} + +/// AddImplicitInclude - Add an implicit #include of the specified file to the +/// predefines buffer. +static void AddImplicitInclude(std::vector<char> &Buf, const std::string &File){ + const char *Inc = "#include \""; + Buf.insert(Buf.end(), Inc, Inc+strlen(Inc)); + Buf.insert(Buf.end(), File.begin(), File.end()); + Buf.push_back('"'); + Buf.push_back('\n'); +} + +/// AddImplicitIncludePTH - Add an implicit #include using the original file +/// used to generate a PTH cache. +static void AddImplicitIncludePTH(std::vector<char> &Buf, Preprocessor & PP) { + PTHManager *P = PP.getPTHManager(); + assert(P && "No PTHManager."); + const char *OriginalFile = P->getOriginalSourceFile(); + + if (!OriginalFile) { + assert(!ImplicitIncludePTH.empty()); + fprintf(stderr, "error: PTH file '%s' does not designate an original " + "source header file for -include-pth\n", + ImplicitIncludePTH.c_str()); + exit (1); + } + + AddImplicitInclude(Buf, OriginalFile); +} + +/// InitializePreprocessor - Initialize the preprocessor getting it and the +/// environment ready to process a single file. This returns true on error. +/// +static bool InitializePreprocessor(Preprocessor &PP, + bool InitializeSourceMgr, + const std::string &InFile) { + FileManager &FileMgr = PP.getFileManager(); + + // Figure out where to get and map in the main file. + SourceManager &SourceMgr = PP.getSourceManager(); + + if (InitializeSourceMgr) { + if (InFile != "-") { + const FileEntry *File = FileMgr.getFile(InFile); + if (File) SourceMgr.createMainFileID(File, SourceLocation()); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) + << InFile.c_str(); + return true; + } + } else { + llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getSTDIN(); + + // If stdin was empty, SB is null. Cons up an empty memory + // buffer now. + if (!SB) { + const char *EmptyStr = ""; + SB = llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<stdin>"); + } + + SourceMgr.createMainFileIDForMemBuffer(SB); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), + diag::err_fe_error_reading_stdin); + return true; + } + } + } + + std::vector<char> PredefineBuffer; + + // Add macros from the command line. + unsigned d = 0, D = D_macros.size(); + unsigned u = 0, U = U_macros.size(); + while (d < D || u < U) { + if (u == U || (d < D && D_macros.getPosition(d) < U_macros.getPosition(u))) + DefineBuiltinMacro(PredefineBuffer, D_macros[d++].c_str()); + else + DefineBuiltinMacro(PredefineBuffer, U_macros[u++].c_str(), "#undef "); + } + + // FIXME: Read any files specified by -imacros. + + // Add implicit #includes from -include and -include-pth. + bool handledPTH = ImplicitIncludePTH.empty(); + for (unsigned i = 0, e = ImplicitIncludes.size(); i != e; ++i) { + if (!handledPTH && + ImplicitIncludePTH.getPosition() < ImplicitIncludes.getPosition(i)) { + AddImplicitIncludePTH(PredefineBuffer, PP); + handledPTH = true; + } + + AddImplicitInclude(PredefineBuffer, ImplicitIncludes[i]); + } + if (!handledPTH && !ImplicitIncludePTH.empty()) + AddImplicitIncludePTH(PredefineBuffer, PP); + + // Null terminate PredefinedBuffer and add it. + PredefineBuffer.push_back(0); + PP.setPredefines(&PredefineBuffer[0]); + + // Once we've read this, we're done. + return false; +} + +//===----------------------------------------------------------------------===// +// Preprocessor include path information. +//===----------------------------------------------------------------------===// + +// This tool exports a large number of command line options to control how the +// preprocessor searches for header files. At root, however, the Preprocessor +// object takes a very simple interface: a list of directories to search for +// +// FIXME: -nostdinc,-nostdinc++ +// FIXME: -imultilib +// + +static llvm::cl::opt<bool> +nostdinc("nostdinc", llvm::cl::desc("Disable standard #include directories")); + +// Various command line options. These four add directories to each chain. +static llvm::cl::list<std::string> +F_dirs("F", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to framework include search path")); +static llvm::cl::list<std::string> +I_dirs("I", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to include search path")); +static llvm::cl::list<std::string> +idirafter_dirs("idirafter", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to AFTER include search path")); +static llvm::cl::list<std::string> +iquote_dirs("iquote", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to QUOTE include search path")); +static llvm::cl::list<std::string> +isystem_dirs("isystem", llvm::cl::value_desc("directory"), llvm::cl::Prefix, + llvm::cl::desc("Add directory to SYSTEM include search path")); + +// These handle -iprefix/-iwithprefix/-iwithprefixbefore. +static llvm::cl::list<std::string> +iprefix_vals("iprefix", llvm::cl::value_desc("prefix"), llvm::cl::Prefix, + llvm::cl::desc("Set the -iwithprefix/-iwithprefixbefore prefix")); +static llvm::cl::list<std::string> +iwithprefix_vals("iwithprefix", llvm::cl::value_desc("dir"), llvm::cl::Prefix, + llvm::cl::desc("Set directory to SYSTEM include search path with prefix")); +static llvm::cl::list<std::string> +iwithprefixbefore_vals("iwithprefixbefore", llvm::cl::value_desc("dir"), + llvm::cl::Prefix, + llvm::cl::desc("Set directory to include search path with prefix")); + +static llvm::cl::opt<std::string> +isysroot("isysroot", llvm::cl::value_desc("dir"), llvm::cl::init("/"), + llvm::cl::desc("Set the system root directory (usually /)")); + +// Finally, implement the code that groks the options above. + +/// InitializeIncludePaths - Process the -I options and set them in the +/// HeaderSearch object. +void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, + FileManager &FM, const LangOptions &Lang) { + InitHeaderSearch Init(Headers, Verbose, isysroot); + + // Handle -I... and -F... options, walking the lists in parallel. + unsigned Iidx = 0, Fidx = 0; + while (Iidx < I_dirs.size() && Fidx < F_dirs.size()) { + if (I_dirs.getPosition(Iidx) < F_dirs.getPosition(Fidx)) { + Init.AddPath(I_dirs[Iidx], InitHeaderSearch::Angled, false, true, false); + ++Iidx; + } else { + Init.AddPath(F_dirs[Fidx], InitHeaderSearch::Angled, false, true, true); + ++Fidx; + } + } + + // Consume what's left from whatever list was longer. + for (; Iidx != I_dirs.size(); ++Iidx) + Init.AddPath(I_dirs[Iidx], InitHeaderSearch::Angled, false, true, false); + for (; Fidx != F_dirs.size(); ++Fidx) + Init.AddPath(F_dirs[Fidx], InitHeaderSearch::Angled, false, true, true); + + // Handle -idirafter... options. + for (unsigned i = 0, e = idirafter_dirs.size(); i != e; ++i) + Init.AddPath(idirafter_dirs[i], InitHeaderSearch::After, + false, true, false); + + // Handle -iquote... options. + for (unsigned i = 0, e = iquote_dirs.size(); i != e; ++i) + Init.AddPath(iquote_dirs[i], InitHeaderSearch::Quoted, false, true, false); + + // Handle -isystem... options. + for (unsigned i = 0, e = isystem_dirs.size(); i != e; ++i) + Init.AddPath(isystem_dirs[i], InitHeaderSearch::System, false, true, false); + + // Walk the -iprefix/-iwithprefix/-iwithprefixbefore argument lists in + // parallel, processing the values in order of occurance to get the right + // prefixes. + { + std::string Prefix = ""; // FIXME: this isn't the correct default prefix. + unsigned iprefix_idx = 0; + unsigned iwithprefix_idx = 0; + unsigned iwithprefixbefore_idx = 0; + bool iprefix_done = iprefix_vals.empty(); + bool iwithprefix_done = iwithprefix_vals.empty(); + bool iwithprefixbefore_done = iwithprefixbefore_vals.empty(); + while (!iprefix_done || !iwithprefix_done || !iwithprefixbefore_done) { + if (!iprefix_done && + (iwithprefix_done || + iprefix_vals.getPosition(iprefix_idx) < + iwithprefix_vals.getPosition(iwithprefix_idx)) && + (iwithprefixbefore_done || + iprefix_vals.getPosition(iprefix_idx) < + iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { + Prefix = iprefix_vals[iprefix_idx]; + ++iprefix_idx; + iprefix_done = iprefix_idx == iprefix_vals.size(); + } else if (!iwithprefix_done && + (iwithprefixbefore_done || + iwithprefix_vals.getPosition(iwithprefix_idx) < + iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { + Init.AddPath(Prefix+iwithprefix_vals[iwithprefix_idx], + InitHeaderSearch::System, false, false, false); + ++iwithprefix_idx; + iwithprefix_done = iwithprefix_idx == iwithprefix_vals.size(); + } else { + Init.AddPath(Prefix+iwithprefixbefore_vals[iwithprefixbefore_idx], + InitHeaderSearch::Angled, false, false, false); + ++iwithprefixbefore_idx; + iwithprefixbefore_done = + iwithprefixbefore_idx == iwithprefixbefore_vals.size(); + } + } + } + + Init.AddDefaultEnvVarPaths(Lang); + + // Add the clang headers, which are relative to the clang binary. + llvm::sys::Path MainExecutablePath = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t)InitializeIncludePaths); + if (!MainExecutablePath.isEmpty()) { + MainExecutablePath.eraseComponent(); // Remove /clang from foo/bin/clang + MainExecutablePath.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/1.0/include + // + // FIXME: Don't embed version here. + MainExecutablePath.appendComponent("lib"); + MainExecutablePath.appendComponent("clang"); + MainExecutablePath.appendComponent("1.0"); + MainExecutablePath.appendComponent("include"); + + // We pass true to ignore sysroot so that we *always* look for clang headers + // relative to our executable, never relative to -isysroot. + Init.AddPath(MainExecutablePath.c_str(), InitHeaderSearch::System, + false, false, false, true /*ignore sysroot*/); + } + + if (!nostdinc) + Init.AddDefaultSystemIncludePaths(Lang); + + // Now that we have collected all of the include paths, merge them all + // together and tell the preprocessor about them. + + Init.Realize(); +} + +//===----------------------------------------------------------------------===// +// Driver PreprocessorFactory - For lazily generating preprocessors ... +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN DriverPreprocessorFactory : public PreprocessorFactory { + const std::string &InFile; + Diagnostic &Diags; + const LangOptions &LangInfo; + TargetInfo &Target; + SourceManager &SourceMgr; + HeaderSearch &HeaderInfo; + bool InitializeSourceMgr; + +public: + DriverPreprocessorFactory(const std::string &infile, + Diagnostic &diags, const LangOptions &opts, + TargetInfo &target, SourceManager &SM, + HeaderSearch &Headers) + : InFile(infile), Diags(diags), LangInfo(opts), Target(target), + SourceMgr(SM), HeaderInfo(Headers), InitializeSourceMgr(true) {} + + + virtual ~DriverPreprocessorFactory() {} + + virtual Preprocessor* CreatePreprocessor() { + llvm::OwningPtr<PTHManager> PTHMgr; + + if (!TokenCache.empty() && !ImplicitIncludePTH.empty()) { + fprintf(stderr, "error: cannot use both -token-cache and -include-pth " + "options\n"); + exit(1); + } + + // Use PTH? + if (!TokenCache.empty() || !ImplicitIncludePTH.empty()) { + const std::string& x = TokenCache.empty() ? ImplicitIncludePTH:TokenCache; + PTHMgr.reset(PTHManager::Create(x, &Diags, + TokenCache.empty() ? Diagnostic::Error + : Diagnostic::Warning)); + } + + if (Diags.hasErrorOccurred()) + exit(1); + + // Create the Preprocessor. + llvm::OwningPtr<Preprocessor> PP(new Preprocessor(Diags, LangInfo, Target, + SourceMgr, HeaderInfo, + PTHMgr.get())); + + // Note that this is different then passing PTHMgr to Preprocessor's ctor. + // That argument is used as the IdentifierInfoLookup argument to + // IdentifierTable's ctor. + if (PTHMgr) { + PTHMgr->setPreprocessor(PP.get()); + PP->setPTHManager(PTHMgr.take()); + } + + if (InitializePreprocessor(*PP, InitializeSourceMgr, InFile)) { + return NULL; + } + + /// FIXME: PP can only handle one callback + if (ProgAction != PrintPreprocessedInput) { + const char* ErrStr; + bool DFG = CreateDependencyFileGen(PP.get(), OutputFile, InFile, ErrStr); + if (!DFG && ErrStr) { + fprintf(stderr, "%s", ErrStr); + return NULL; + } + } + + InitializeSourceMgr = false; + return PP.take(); + } +}; +} + +//===----------------------------------------------------------------------===// +// Basic Parser driver +//===----------------------------------------------------------------------===// + +static void ParseFile(Preprocessor &PP, MinimalAction *PA) { + Parser P(PP, *PA); + PP.EnterMainSourceFile(); + + // Parsing the specified input file. + P.ParseTranslationUnit(); + delete PA; +} + +//===----------------------------------------------------------------------===// +// Code generation options +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +GenerateDebugInfo("g", + llvm::cl::desc("Generate source level debug information")); + +static llvm::cl::opt<bool> +OptSize("Os", llvm::cl::desc("Optimize for size")); + +// It might be nice to add bounds to the CommandLine library directly. +struct OptLevelParser : public llvm::cl::parser<unsigned> { + bool parse(llvm::cl::Option &O, const char *ArgName, + const std::string &Arg, unsigned &Val) { + if (llvm::cl::parser<unsigned>::parse(O, ArgName, Arg, Val)) + return true; + // FIXME: Support -O4. + if (Val > 3) + return O.error(": '" + Arg + "' invalid optimization level!"); + return false; + } +}; +static llvm::cl::opt<unsigned, false, OptLevelParser> +OptLevel("O", llvm::cl::Prefix, + llvm::cl::desc("Optimization level"), + llvm::cl::init(0)); + +static llvm::cl::opt<std::string> +TargetCPU("mcpu", + llvm::cl::desc("Target a specific cpu type (-mcpu=help for details)")); + +static void InitializeCompileOptions(CompileOptions &Opts, + const LangOptions &LangOpts) { + Opts.OptimizeSize = OptSize; + Opts.DebugInfo = GenerateDebugInfo; + if (OptSize) { + // -Os implies -O2 + // FIXME: Diagnose conflicting options. + Opts.OptimizationLevel = 2; + } else { + Opts.OptimizationLevel = OptLevel; + } + + // FIXME: There are llvm-gcc options to control these selectively. + Opts.InlineFunctions = (Opts.OptimizationLevel > 1); + Opts.UnrollLoops = (Opts.OptimizationLevel > 1 && !OptSize); + Opts.SimplifyLibCalls = !LangOpts.NoBuiltin; + +#ifdef NDEBUG + Opts.VerifyModule = 0; +#endif + + Opts.CPU = TargetCPU; + Opts.Features.insert(Opts.Features.end(), + TargetFeatures.begin(), TargetFeatures.end()); + + // Handle -ftime-report. + Opts.TimePasses = TimeReport; +} + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +/// CreateASTConsumer - Create the ASTConsumer for the corresponding program +/// action. These consumers can operate on both ASTs that are freshly +/// parsed from source files as well as those deserialized from Bitcode. +/// Note that PP and PPF may be null here. +static ASTConsumer *CreateASTConsumer(const std::string& InFile, + Diagnostic& Diag, FileManager& FileMgr, + const LangOptions& LangOpts, + Preprocessor *PP, + PreprocessorFactory *PPF) { + switch (ProgAction) { + default: + return NULL; + + case ASTPrint: + return CreateASTPrinter(); + + case ASTDump: + return CreateASTDumper(); + + case ASTView: + return CreateASTViewer(); + + case PrintDeclContext: + return CreateDeclContextPrinter(); + + case EmitHTML: + return CreateHTMLPrinter(OutputFile, Diag, PP, PPF); + + case InheritanceView: + return CreateInheritanceViewer(InheritanceViewCls); + + case TestSerialization: + return CreateSerializationTest(Diag, FileMgr); + + case EmitAssembly: + case EmitLLVM: + case EmitBC: + case EmitLLVMOnly: { + BackendAction Act; + if (ProgAction == EmitAssembly) + Act = Backend_EmitAssembly; + else if (ProgAction == EmitLLVM) + Act = Backend_EmitLL; + else if (ProgAction == EmitLLVMOnly) + Act = Backend_EmitNothing; + else + Act = Backend_EmitBC; + + CompileOptions Opts; + InitializeCompileOptions(Opts, LangOpts); + return CreateBackendConsumer(Act, Diag, LangOpts, Opts, + InFile, OutputFile); + } + + case SerializeAST: + // FIXME: Allow user to tailor where the file is written. + return CreateASTSerializer(InFile, OutputFile, Diag); + + case RewriteObjC: + return CreateCodeRewriterTest(InFile, OutputFile, Diag, LangOpts); + + case RewriteBlocks: + return CreateBlockRewriter(InFile, OutputFile, Diag, LangOpts); + + case RunAnalysis: + return CreateAnalysisConsumer(Diag, PP, PPF, LangOpts, OutputFile); + } +} + +/// ProcessInputFile - Process a single input file with the specified state. +/// +static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, + const std::string &InFile, ProgActions PA) { + llvm::OwningPtr<ASTConsumer> Consumer; + bool ClearSourceMgr = false; + + switch (PA) { + default: + Consumer.reset(CreateASTConsumer(InFile, PP.getDiagnostics(), + PP.getFileManager(), PP.getLangOptions(), + &PP, &PPF)); + + if (!Consumer) { + fprintf(stderr, "Unexpected program action!\n"); + HadErrors = true; + return; + } + + break; + + case DumpRawTokens: { + llvm::TimeRegion Timer(ClangFrontendTimer); + SourceManager &SM = PP.getSourceManager(); + // Start lexing the specified input file. + Lexer RawLex(SM.getMainFileID(), SM, PP.getLangOptions()); + RawLex.SetKeepWhitespaceMode(true); + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + PP.DumpToken(RawTok, true); + fprintf(stderr, "\n"); + RawLex.LexFromRawLexer(RawTok); + } + ClearSourceMgr = true; + break; + } + case DumpTokens: { // Token dump mode. + llvm::TimeRegion Timer(ClangFrontendTimer); + Token Tok; + // Start preprocessing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + PP.DumpToken(Tok, true); + fprintf(stderr, "\n"); + } while (Tok.isNot(tok::eof)); + ClearSourceMgr = true; + break; + } + case RunPreprocessorOnly: { // Just lex as fast as we can, no output. + llvm::TimeRegion Timer(ClangFrontendTimer); + Token Tok; + // Start parsing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + ClearSourceMgr = true; + break; + } + + case GeneratePCH: { + llvm::TimeRegion Timer(ClangFrontendTimer); + CacheTokens(PP, OutputFile); + ClearSourceMgr = true; + break; + } + + case PrintPreprocessedInput: { // -E mode. + llvm::TimeRegion Timer(ClangFrontendTimer); + DoPrintPreprocessedInput(PP, OutputFile); + ClearSourceMgr = true; + break; + } + + case ParseNoop: { // -parse-noop + llvm::TimeRegion Timer(ClangFrontendTimer); + ParseFile(PP, new MinimalAction(PP)); + ClearSourceMgr = true; + break; + } + + case ParsePrintCallbacks: { + llvm::TimeRegion Timer(ClangFrontendTimer); + ParseFile(PP, CreatePrintParserActionsAction(PP)); + ClearSourceMgr = true; + break; + } + + case ParseSyntaxOnly: { // -fsyntax-only + llvm::TimeRegion Timer(ClangFrontendTimer); + Consumer.reset(new ASTConsumer()); + break; + } + + case RewriteMacros: + RewriteMacrosInInput(PP, InFile, OutputFile); + ClearSourceMgr = true; + break; + + case RewriteTest: { + DoRewriteTest(PP, InFile, OutputFile); + ClearSourceMgr = true; + break; + } + } + + if (Consumer) { + TranslationUnit *TU = 0; + if (DisableFree) { + ASTContext *Context = new ASTContext(PP.getLangOptions(), + PP.getSourceManager(), + PP.getTargetInfo(), + PP.getIdentifierTable(), + PP.getSelectorTable(), + /* FreeMemory = */ false); + TU = new TranslationUnit(*Context); + } + ParseAST(PP, Consumer.get(), TU, Stats); + } + + if (VerifyDiagnostics) + if (CheckDiagnostics(PP)) + exit(1); + + if (Stats) { + fprintf(stderr, "\nSTATISTICS FOR '%s':\n", InFile.c_str()); + PP.PrintStats(); + PP.getIdentifierTable().PrintStats(); + PP.getHeaderSearchInfo().PrintStats(); + PP.getSourceManager().PrintStats(); + fprintf(stderr, "\n"); + } + + // For a multi-file compilation, some things are ok with nuking the source + // manager tables, other require stable fileid/macroid's across multiple + // files. + if (ClearSourceMgr) + PP.getSourceManager().clearIDTables(); + + if (DisableFree) + Consumer.take(); +} + +static void ProcessSerializedFile(const std::string& InFile, Diagnostic& Diag, + FileManager& FileMgr) { + + if (VerifyDiagnostics) { + fprintf(stderr, "-verify does not yet work with serialized ASTs.\n"); + exit (1); + } + + llvm::sys::Path Filename(InFile); + + if (!Filename.isValid()) { + fprintf(stderr, "serialized file '%s' not available.\n",InFile.c_str()); + exit (1); + } + + llvm::OwningPtr<TranslationUnit> TU(ReadASTBitcodeFile(Filename, FileMgr)); + + if (!TU) { + fprintf(stderr, "error: file '%s' could not be deserialized\n", + InFile.c_str()); + exit (1); + } + + // Observe that we use the source file name stored in the deserialized + // translation unit, rather than InFile. + llvm::OwningPtr<ASTConsumer> + Consumer(CreateASTConsumer(InFile, Diag, FileMgr, TU->getLangOptions(), + 0, 0)); + + if (!Consumer) { + fprintf(stderr, "Unsupported program action with serialized ASTs!\n"); + exit (1); + } + + Consumer->Initialize(TU->getContext()); + + // FIXME: We need to inform Consumer about completed TagDecls as well. + for (TranslationUnit::iterator I=TU->begin(), E=TU->end(); I!=E; ++I) + Consumer->HandleTopLevelDecl(*I); +} + + +static llvm::cl::list<std::string> +InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input files>")); + +static bool isSerializedFile(const std::string& InFile) { + if (InFile.size() < 4) + return false; + + const char* s = InFile.c_str()+InFile.size()-4; + return s[0] == '.' && s[1] == 'a' && s[2] == 's' && s[3] == 't'; +} + + +int main(int argc, char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + llvm::cl::ParseCommandLineOptions(argc, argv, + "LLVM 'Clang' Compiler: http://clang.llvm.org\n"); + + if (TimeReport) + ClangFrontendTimer = new llvm::Timer("Clang front-end time"); + + // If no input was specified, read from stdin. + if (InputFilenames.empty()) + InputFilenames.push_back("-"); + + // Create a file manager object to provide access to and cache the filesystem. + FileManager FileMgr; + + // Create the diagnostic client for reporting errors or for + // implementing -verify. + DiagnosticClient* TextDiagClient = 0; + + if (!VerifyDiagnostics) { + // Print diagnostics to stderr by default. + TextDiagClient = new TextDiagnosticPrinter(llvm::errs(), + !NoShowColumn, + !NoCaretDiagnostics, + !NoShowLocation, + PrintSourceRangeInfo); + } else { + // When checking diagnostics, just buffer them up. + TextDiagClient = new TextDiagnosticBuffer(); + + if (InputFilenames.size() != 1) { + fprintf(stderr, + "-verify only works on single input files for now.\n"); + return 1; + } + } + + // Configure our handling of diagnostics. + llvm::OwningPtr<DiagnosticClient> DiagClient(TextDiagClient); + Diagnostic Diags(DiagClient.get()); + if (ProcessWarningOptions(Diags)) + return 1; + + // -I- is a deprecated GCC feature, scan for it and reject it. + for (unsigned i = 0, e = I_dirs.size(); i != e; ++i) { + if (I_dirs[i] == "-") { + Diags.Report(FullSourceLoc(), diag::err_pp_I_dash_not_supported); + I_dirs.erase(I_dirs.begin()+i); + --i; + } + } + + // Get information about the target being compiled for. + std::string Triple = CreateTargetTriple(); + llvm::OwningPtr<TargetInfo> Target(TargetInfo::CreateTargetInfo(Triple)); + + if (Target == 0) { + Diags.Report(FullSourceLoc(), diag::err_fe_unknown_triple) + << Triple.c_str(); + return 1; + } + + if (!InheritanceViewCls.empty()) // C++ visualization? + ProgAction = InheritanceView; + + llvm::OwningPtr<SourceManager> SourceMgr; + + for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { + const std::string &InFile = InputFilenames[i]; + + if (isSerializedFile(InFile)) { + Diags.setClient(TextDiagClient); + ProcessSerializedFile(InFile,Diags,FileMgr); + continue; + } + + /// Create a SourceManager object. This tracks and owns all the file + /// buffers allocated to a translation unit. + if (!SourceMgr) + SourceMgr.reset(new SourceManager()); + else + SourceMgr->clearIDTables(); + + // Initialize language options, inferring file types from input filenames. + LangOptions LangInfo; + InitializeBaseLanguage(); + LangKind LK = GetLanguage(InFile); + bool PCH = InitializeLangOptions(LangInfo, LK); + InitializeGCMode(LangInfo); + InitializeLanguageStandard(LangInfo, LK, Target.get()); + + // Process the -I options and set them in the HeaderInfo. + HeaderSearch HeaderInfo(FileMgr); + + InitializeIncludePaths(argv[0], HeaderInfo, FileMgr, LangInfo); + + // Set up the preprocessor with these options. + DriverPreprocessorFactory PPFactory(InFile, Diags, LangInfo, *Target, + *SourceMgr.get(), HeaderInfo); + + llvm::OwningPtr<Preprocessor> PP(PPFactory.CreatePreprocessor()); + + if (!PP) + continue; + + // Create the HTMLDiagnosticsClient if we are using one. Otherwise, + // always reset to using TextDiagClient. + llvm::OwningPtr<DiagnosticClient> TmpClient; + + if (!HTMLDiag.empty()) { + TmpClient.reset(CreateHTMLDiagnosticClient(HTMLDiag, PP.get(), + &PPFactory)); + Diags.setClient(TmpClient.get()); + } + else + Diags.setClient(TextDiagClient); + + // Process the source file. + ProcessInputFile(*PP, PPFactory, InFile, PCH ? GeneratePCH : ProgAction); + + HeaderInfo.ClearFileInfo(); + } + + if (Verbose) + fprintf(stderr, "clang version 1.0 based upon " PACKAGE_STRING + " hosted on " LLVM_HOSTTRIPLE "\n"); + + if (unsigned NumDiagnostics = Diags.getNumDiagnostics()) + fprintf(stderr, "%d diagnostic%s generated.\n", NumDiagnostics, + (NumDiagnostics == 1 ? "" : "s")); + + if (Stats) { + FileMgr.PrintStats(); + fprintf(stderr, "\n"); + } + + // If verifying diagnostics and we reached here, all is well. + if (VerifyDiagnostics) + return 0; + + delete ClangFrontendTimer; + + // Managed static deconstruction. Useful for making things like + // -time-passes usable. + llvm::llvm_shutdown(); + + return HadErrors || (Diags.getNumErrors() != 0); +} diff --git a/clang/tools/clang-cc/clang.h b/clang/tools/clang-cc/clang.h new file mode 100644 index 00000000000..33bc7fe5832 --- /dev/null +++ b/clang/tools/clang-cc/clang.h @@ -0,0 +1,63 @@ +//===--- clang.h - C-Language Front-end -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the header file that pulls together the top-level driver. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CLANG_H +#define LLVM_CLANG_CLANG_H + +#include <vector> +#include <string> + +namespace clang { +class Preprocessor; +class MinimalAction; +class TargetInfo; +class Diagnostic; +class ASTConsumer; +class IdentifierTable; +class SourceManager; + +/// ProcessWarningOptions - Initialize the diagnostic client and process the +/// warning options specified on the command line. +bool ProcessWarningOptions(Diagnostic &Diags); + +/// DoPrintPreprocessedInput - Implement -E mode. +void DoPrintPreprocessedInput(Preprocessor &PP, const std::string& OutFile); + +/// RewriteMacrosInInput - Implement -rewrite-macros mode. +void RewriteMacrosInInput(Preprocessor &PP, const std::string &InFileName, + const std::string& OutFile); + +void DoRewriteTest(Preprocessor &PP, const std::string &InFileName, + const std::string &OutFileName); + + +/// CreatePrintParserActionsAction - Return the actions implementation that +/// implements the -parse-print-callbacks option. +MinimalAction *CreatePrintParserActionsAction(Preprocessor &PP); + +/// CheckDiagnostics - Gather the expected diagnostics and check them. +bool CheckDiagnostics(Preprocessor &PP); + +/// CreateDependencyFileGen - Create dependency file generator. +/// This is only done if either -MD or -MMD has been specified. +bool CreateDependencyFileGen(Preprocessor *PP, + std::string &OutputFile, + const std::string &InputFile, + const char *&ErrStr); + +/// CacheTokens - Cache tokens for use with PCH. +void CacheTokens(Preprocessor& PP, const std::string& OutFile); + +} // end namespace clang + +#endif |

