diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend')
39 files changed, 32719 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp new file mode 100644 index 0000000..f53c614 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp @@ -0,0 +1,490 @@ +//===--- 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 "clang/Frontend/ASTConsumers.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +/// ASTPrinter - Pretty-printer and dumper of ASTs + +namespace { + class ASTPrinter : public ASTConsumer, + public RecursiveASTVisitor<ASTPrinter> { + typedef RecursiveASTVisitor<ASTPrinter> base; + + public: + ASTPrinter(raw_ostream *Out = nullptr, bool Dump = false, + StringRef FilterString = "", bool DumpLookups = false) + : Out(Out ? *Out : llvm::outs()), Dump(Dump), + FilterString(FilterString), DumpLookups(DumpLookups) {} + + void HandleTranslationUnit(ASTContext &Context) override { + TranslationUnitDecl *D = Context.getTranslationUnitDecl(); + + if (FilterString.empty()) + return print(D); + + TraverseDecl(D); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseDecl(Decl *D) { + if (D && filterMatches(D)) { + bool ShowColors = Out.has_colors(); + if (ShowColors) + Out.changeColor(raw_ostream::BLUE); + Out << ((Dump || DumpLookups) ? "Dumping " : "Printing ") << getName(D) + << ":\n"; + if (ShowColors) + Out.resetColor(); + print(D); + Out << "\n"; + // Don't traverse child nodes to avoid output duplication. + return true; + } + return base::TraverseDecl(D); + } + + private: + std::string getName(Decl *D) { + if (isa<NamedDecl>(D)) + return cast<NamedDecl>(D)->getQualifiedNameAsString(); + return ""; + } + bool filterMatches(Decl *D) { + return getName(D).find(FilterString) != std::string::npos; + } + void print(Decl *D) { + if (DumpLookups) { + if (DeclContext *DC = dyn_cast<DeclContext>(D)) { + if (DC == DC->getPrimaryContext()) + DC->dumpLookups(Out, Dump); + else + Out << "Lookup map is in primary DeclContext " + << DC->getPrimaryContext() << "\n"; + } else + Out << "Not a DeclContext\n"; + } else if (Dump) + D->dump(Out); + else + D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true); + } + + raw_ostream &Out; + bool Dump; + std::string FilterString; + bool DumpLookups; + }; + + class ASTDeclNodeLister : public ASTConsumer, + public RecursiveASTVisitor<ASTDeclNodeLister> { + public: + ASTDeclNodeLister(raw_ostream *Out = nullptr) + : Out(Out ? *Out : llvm::outs()) {} + + void HandleTranslationUnit(ASTContext &Context) override { + TraverseDecl(Context.getTranslationUnitDecl()); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitNamedDecl(NamedDecl *D) { + D->printQualifiedName(Out); + Out << '\n'; + return true; + } + + private: + raw_ostream &Out; + }; +} // end anonymous namespace + +std::unique_ptr<ASTConsumer> clang::CreateASTPrinter(raw_ostream *Out, + StringRef FilterString) { + return llvm::make_unique<ASTPrinter>(Out, /*Dump=*/false, FilterString); +} + +std::unique_ptr<ASTConsumer> clang::CreateASTDumper(StringRef FilterString, + bool DumpDecls, + bool DumpLookups) { + assert((DumpDecls || DumpLookups) && "nothing to dump"); + return llvm::make_unique<ASTPrinter>(nullptr, DumpDecls, FilterString, + DumpLookups); +} + +std::unique_ptr<ASTConsumer> clang::CreateASTDeclNodeLister() { + return llvm::make_unique<ASTDeclNodeLister>(nullptr); +} + +//===----------------------------------------------------------------------===// +/// ASTViewer - AST Visualization + +namespace { + class ASTViewer : public ASTConsumer { + ASTContext *Context; + public: + void Initialize(ASTContext &Context) override { + this->Context = &Context; + } + + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) + HandleTopLevelSingleDecl(*I); + return true; + } + + void HandleTopLevelSingleDecl(Decl *D); + }; +} + +void ASTViewer::HandleTopLevelSingleDecl(Decl *D) { + if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + D->print(llvm::errs()); + + if (Stmt *Body = D->getBody()) { + llvm::errs() << '\n'; + Body->viewAST(); + llvm::errs() << '\n'; + } + } +} + +std::unique_ptr<ASTConsumer> clang::CreateASTViewer() { + return llvm::make_unique<ASTViewer>(); +} + +//===----------------------------------------------------------------------===// +/// DeclContextPrinter - Decl and DeclContext Visualization + +namespace { + +class DeclContextPrinter : public ASTConsumer { + raw_ostream& Out; +public: + DeclContextPrinter() : Out(llvm::errs()) {} + + void HandleTranslationUnit(ASTContext &C) override { + PrintDeclContext(C.getTranslationUnitDecl(), 4); + } + + void PrintDeclContext(const DeclContext* DC, unsigned Indentation); +}; +} // end anonymous namespace + +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; + break; + } + case Decl::Enum: { + const EnumDecl* ED = cast<EnumDecl>(DC); + if (ED->isCompleteDefinition()) + Out << "[enum] "; + else + Out << "<enum> "; + Out << *ED; + break; + } + case Decl::Record: { + const RecordDecl* RD = cast<RecordDecl>(DC); + if (RD->isCompleteDefinition()) + Out << "[struct] "; + else + Out << "<struct> "; + Out << *RD; + break; + } + case Decl::CXXRecord: { + const CXXRecordDecl* RD = cast<CXXRecordDecl>(DC); + if (RD->isCompleteDefinition()) + Out << "[class] "; + else + Out << "<class> "; + Out << *RD << ' ' << 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->doesThisDeclarationHaveABody()) + Out << "[function] "; + else + Out << "<function> "; + Out << *FD; + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (auto I : FD->params()) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << *I; + } + Out << ")"; + break; + } + case Decl::CXXMethod: { + const CXXMethodDecl* D = cast<CXXMethodDecl>(DC); + if (D->isOutOfLine()) + Out << "[c++ method] "; + else if (D->isImplicit()) + Out << "(c++ method) "; + else + Out << "<c++ method> "; + Out << *D; + // 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; + } + 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->isOutOfLine()) + Out << "[c++ ctor] "; + else if (D->isImplicit()) + Out << "(c++ ctor) "; + else + Out << "<c++ ctor> "; + Out << *D; + // 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; + } + 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->isOutOfLine()) + Out << "[c++ dtor] "; + else if (D->isImplicit()) + Out << "(c++ dtor) "; + else + Out << "<c++ dtor> "; + Out << *D; + // 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->isOutOfLine()) + Out << "[c++ conversion] "; + else if (D->isImplicit()) + Out << "(c++ conversion) "; + else + Out << "<c++ conversion> "; + Out << *D; + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + + default: + llvm_unreachable("a decl that inherits DeclContext isn't handled"); + } + + Out << "\n"; + + // Print decls in the DeclContext. + for (auto *I : DC->decls()) { + 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::IndirectField: { + IndirectFieldDecl* IFD = cast<IndirectFieldDecl>(I); + Out << "<IndirectField> " << *IFD << '\n'; + break; + } + case Decl::Label: { + LabelDecl *LD = cast<LabelDecl>(I); + Out << "<Label> " << *LD << '\n'; + break; + } + case Decl::Field: { + FieldDecl *FD = cast<FieldDecl>(I); + Out << "<field> " << *FD << '\n'; + break; + } + case Decl::Typedef: + case Decl::TypeAlias: { + TypedefNameDecl* TD = cast<TypedefNameDecl>(I); + Out << "<typedef> " << *TD << '\n'; + break; + } + case Decl::EnumConstant: { + EnumConstantDecl* ECD = cast<EnumConstantDecl>(I); + Out << "<enum constant> " << *ECD << '\n'; + break; + } + case Decl::Var: { + VarDecl* VD = cast<VarDecl>(I); + Out << "<var> " << *VD << '\n'; + break; + } + case Decl::ImplicitParam: { + ImplicitParamDecl* IPD = cast<ImplicitParamDecl>(I); + Out << "<implicit parameter> " << *IPD << '\n'; + break; + } + case Decl::ParmVar: { + ParmVarDecl* PVD = cast<ParmVarDecl>(I); + Out << "<parameter> " << *PVD << '\n'; + break; + } + case Decl::ObjCProperty: { + ObjCPropertyDecl* OPD = cast<ObjCPropertyDecl>(I); + Out << "<objc property> " << *OPD << '\n'; + break; + } + case Decl::FunctionTemplate: { + FunctionTemplateDecl* FTD = cast<FunctionTemplateDecl>(I); + Out << "<function template> " << *FTD << '\n'; + break; + } + case Decl::FileScopeAsm: { + Out << "<file-scope asm>\n"; + break; + } + case Decl::UsingDirective: { + Out << "<using directive>\n"; + break; + } + case Decl::NamespaceAlias: { + NamespaceAliasDecl* NAD = cast<NamespaceAliasDecl>(I); + Out << "<namespace alias> " << *NAD << '\n'; + break; + } + case Decl::ClassTemplate: { + ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(I); + Out << "<class template> " << *CTD << '\n'; + break; + } + case Decl::OMPThreadPrivate: { + Out << "<omp threadprivate> " << '"' << I << "\"\n"; + break; + } + default: + Out << "DeclKind: " << DK << '"' << I << "\"\n"; + llvm_unreachable("decl unhandled"); + } + } +} +std::unique_ptr<ASTConsumer> clang::CreateDeclContextPrinter() { + return llvm::make_unique<DeclContextPrinter>(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp new file mode 100644 index 0000000..216ac6a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp @@ -0,0 +1,107 @@ +//===-- ASTMerge.cpp - AST Merging Frontent Action --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporter.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" + +using namespace clang; + +std::unique_ptr<ASTConsumer> +ASTMergeAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return AdaptedAction->CreateASTConsumer(CI, InFile); +} + +bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // FIXME: This is a hack. We need a better way to communicate the + // AST file, compiler instance, and file name than member variables + // of FrontendAction. + AdaptedAction->setCurrentInput(getCurrentInput(), takeCurrentASTUnit()); + AdaptedAction->setCompilerInstance(&CI); + return AdaptedAction->BeginSourceFileAction(CI, Filename); +} + +void ASTMergeAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + CI.getDiagnostics().getClient()->BeginSourceFile( + CI.getASTContext().getLangOpts()); + CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, + &CI.getASTContext()); + IntrusiveRefCntPtr<DiagnosticIDs> + DiagIDs(CI.getDiagnostics().getDiagnosticIDs()); + for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) { + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(new DiagnosticsEngine(DiagIDs, &CI.getDiagnosticOpts(), + new ForwardingDiagnosticConsumer( + *CI.getDiagnostics().getClient()), + /*ShouldOwnClient=*/true)); + std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile( + ASTFiles[I], Diags, CI.getFileSystemOpts(), false); + if (!Unit) + continue; + + ASTImporter Importer(CI.getASTContext(), + CI.getFileManager(), + Unit->getASTContext(), + Unit->getFileManager(), + /*MinimalImport=*/false); + + TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); + for (auto *D : TU->decls()) { + // Don't re-import __va_list_tag, __builtin_va_list. + if (const auto *ND = dyn_cast<NamedDecl>(D)) + if (IdentifierInfo *II = ND->getIdentifier()) + if (II->isStr("__va_list_tag") || II->isStr("__builtin_va_list")) + continue; + + Importer.Import(D); + } + } + + AdaptedAction->ExecuteAction(); + CI.getDiagnostics().getClient()->EndSourceFile(); +} + +void ASTMergeAction::EndSourceFileAction() { + return AdaptedAction->EndSourceFileAction(); +} + +ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction, + ArrayRef<std::string> ASTFiles) + : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { + assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); +} + +ASTMergeAction::~ASTMergeAction() { + delete AdaptedAction; +} + +bool ASTMergeAction::usesPreprocessorOnly() const { + return AdaptedAction->usesPreprocessorOnly(); +} + +TranslationUnitKind ASTMergeAction::getTranslationUnitKind() { + return AdaptedAction->getTranslationUnitKind(); +} + +bool ASTMergeAction::hasPCHSupport() const { + return AdaptedAction->hasPCHSupport(); +} + +bool ASTMergeAction::hasASTFileSupport() const { + return AdaptedAction->hasASTFileSupport(); +} + +bool ASTMergeAction::hasCodeCompletionSupport() const { + return AdaptedAction->hasCodeCompletionSupport(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp new file mode 100644 index 0000000..a3998fa --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp @@ -0,0 +1,2858 @@ +//===--- ASTUnit.cpp - ASTUnit utility ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ASTUnit Implementation. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeOrdering.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <atomic> +#include <cstdio> +#include <cstdlib> +using namespace clang; + +using llvm::TimeRecord; + +namespace { + class SimpleTimer { + bool WantTiming; + TimeRecord Start; + std::string Output; + + public: + explicit SimpleTimer(bool WantTiming) : WantTiming(WantTiming) { + if (WantTiming) + Start = TimeRecord::getCurrentTime(); + } + + void setOutput(const Twine &Output) { + if (WantTiming) + this->Output = Output.str(); + } + + ~SimpleTimer() { + if (WantTiming) { + TimeRecord Elapsed = TimeRecord::getCurrentTime(); + Elapsed -= Start; + llvm::errs() << Output << ':'; + Elapsed.print(Elapsed, llvm::errs()); + llvm::errs() << '\n'; + } + } + }; + + struct OnDiskData { + /// \brief The file in which the precompiled preamble is stored. + std::string PreambleFile; + + /// \brief Temporary files that should be removed when the ASTUnit is + /// destroyed. + SmallVector<std::string, 4> TemporaryFiles; + + /// \brief Erase temporary files. + void CleanTemporaryFiles(); + + /// \brief Erase the preamble file. + void CleanPreambleFile(); + + /// \brief Erase temporary files and the preamble file. + void Cleanup(); + }; +} + +static llvm::sys::SmartMutex<false> &getOnDiskMutex() { + static llvm::sys::SmartMutex<false> M(/* recursive = */ true); + return M; +} + +static void cleanupOnDiskMapAtExit(); + +typedef llvm::DenseMap<const ASTUnit *, + std::unique_ptr<OnDiskData>> OnDiskDataMap; +static OnDiskDataMap &getOnDiskDataMap() { + static OnDiskDataMap M; + static bool hasRegisteredAtExit = false; + if (!hasRegisteredAtExit) { + hasRegisteredAtExit = true; + atexit(cleanupOnDiskMapAtExit); + } + return M; +} + +static void cleanupOnDiskMapAtExit() { + // Use the mutex because there can be an alive thread destroying an ASTUnit. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + for (OnDiskDataMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + // We don't worry about freeing the memory associated with OnDiskDataMap. + // All we care about is erasing stale files. + I->second->Cleanup(); + } +} + +static OnDiskData &getOnDiskData(const ASTUnit *AU) { + // We require the mutex since we are modifying the structure of the + // DenseMap. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + auto &D = M[AU]; + if (!D) + D = llvm::make_unique<OnDiskData>(); + return *D; +} + +static void erasePreambleFile(const ASTUnit *AU) { + getOnDiskData(AU).CleanPreambleFile(); +} + +static void removeOnDiskEntry(const ASTUnit *AU) { + // We require the mutex since we are modifying the structure of the + // DenseMap. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + OnDiskDataMap::iterator I = M.find(AU); + if (I != M.end()) { + I->second->Cleanup(); + M.erase(AU); + } +} + +static void setPreambleFile(const ASTUnit *AU, StringRef preambleFile) { + getOnDiskData(AU).PreambleFile = preambleFile; +} + +static const std::string &getPreambleFile(const ASTUnit *AU) { + return getOnDiskData(AU).PreambleFile; +} + +void OnDiskData::CleanTemporaryFiles() { + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + llvm::sys::fs::remove(TemporaryFiles[I]); + TemporaryFiles.clear(); +} + +void OnDiskData::CleanPreambleFile() { + if (!PreambleFile.empty()) { + llvm::sys::fs::remove(PreambleFile); + PreambleFile.clear(); + } +} + +void OnDiskData::Cleanup() { + CleanTemporaryFiles(); + CleanPreambleFile(); +} + +struct ASTUnit::ASTWriterData { + SmallString<128> Buffer; + llvm::BitstreamWriter Stream; + ASTWriter Writer; + + ASTWriterData() : Stream(Buffer), Writer(Stream) { } +}; + +void ASTUnit::clearFileLevelDecls() { + llvm::DeleteContainerSeconds(FileDecls); +} + +void ASTUnit::CleanTemporaryFiles() { + getOnDiskData(this).CleanTemporaryFiles(); +} + +void ASTUnit::addTemporaryFile(StringRef TempFile) { + getOnDiskData(this).TemporaryFiles.push_back(TempFile); +} + +/// \brief After failing to build a precompiled preamble (due to +/// errors in the source that occurs in the preamble), the number of +/// reparses during which we'll skip even trying to precompile the +/// preamble. +const unsigned DefaultPreambleRebuildInterval = 5; + +/// \brief Tracks the number of ASTUnit objects that are currently active. +/// +/// Used for debugging purposes only. +static std::atomic<unsigned> ActiveASTUnitObjects; + +ASTUnit::ASTUnit(bool _MainFileIsAST) + : Reader(nullptr), HadModuleLoaderFatalFailure(false), + OnlyLocalDecls(false), CaptureDiagnostics(false), + MainFileIsAST(_MainFileIsAST), + TUKind(TU_Complete), WantTiming(getenv("LIBCLANG_TIMING")), + OwnsRemappedFileBuffers(true), + NumStoredDiagnosticsFromDriver(0), + PreambleRebuildCounter(0), + NumWarningsInPreamble(0), + ShouldCacheCodeCompletionResults(false), + IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), + CompletionCacheTopLevelHashValue(0), + PreambleTopLevelHashValue(0), + CurrentTopLevelHashValue(0), + UnsafeToFree(false) { + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "+++ %u translation units\n", ++ActiveASTUnitObjects); +} + +ASTUnit::~ASTUnit() { + // If we loaded from an AST file, balance out the BeginSourceFile call. + if (MainFileIsAST && getDiagnostics().getClient()) { + getDiagnostics().getClient()->EndSourceFile(); + } + + clearFileLevelDecls(); + + // Clean up the temporary files and the preamble file. + removeOnDiskEntry(this); + + // Free the buffers associated with remapped files. We are required to + // perform this operation here because we explicitly request that the + // compiler instance *not* free these buffers for each invocation of the + // parser. + if (Invocation.get() && OwnsRemappedFileBuffers) { + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (const auto &RB : PPOpts.RemappedFileBuffers) + delete RB.second; + } + + ClearCachedCompletionResults(); + + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "--- %u translation units\n", --ActiveASTUnitObjects); +} + +void ASTUnit::setPreprocessor(Preprocessor *pp) { PP = pp; } + +/// \brief Determine the set of code-completion contexts in which this +/// declaration should be shown. +static unsigned getDeclShowContexts(const NamedDecl *ND, + const LangOptions &LangOpts, + bool &IsNestedNameSpecifier) { + IsNestedNameSpecifier = false; + + if (isa<UsingShadowDecl>(ND)) + ND = dyn_cast<NamedDecl>(ND->getUnderlyingDecl()); + if (!ND) + return 0; + + uint64_t Contexts = 0; + if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND) || + isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND)) { + // Types can appear in these contexts. + if (LangOpts.CPlusPlus || !isa<TagDecl>(ND)) + Contexts |= (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); + + // In C++, types can appear in expressions contexts (for functional casts). + if (LangOpts.CPlusPlus) + Contexts |= (1LL << CodeCompletionContext::CCC_Expression); + + // In Objective-C, message sends can send interfaces. In Objective-C++, + // all types are available due to functional casts. + if (LangOpts.CPlusPlus || isa<ObjCInterfaceDecl>(ND)) + Contexts |= (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); + + // In Objective-C, you can only be a subclass of another Objective-C class + if (isa<ObjCInterfaceDecl>(ND)) + Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName); + + // Deal with tag names. + if (isa<EnumDecl>(ND)) { + Contexts |= (1LL << CodeCompletionContext::CCC_EnumTag); + + // Part of the nested-name-specifier in C++0x. + if (LangOpts.CPlusPlus11) + IsNestedNameSpecifier = true; + } else if (const RecordDecl *Record = dyn_cast<RecordDecl>(ND)) { + if (Record->isUnion()) + Contexts |= (1LL << CodeCompletionContext::CCC_UnionTag); + else + Contexts |= (1LL << CodeCompletionContext::CCC_ClassOrStructTag); + + if (LangOpts.CPlusPlus) + IsNestedNameSpecifier = true; + } else if (isa<ClassTemplateDecl>(ND)) + IsNestedNameSpecifier = true; + } else if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) { + // Values can appear in these contexts. + Contexts = (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); + } else if (isa<ObjCProtocolDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_ObjCProtocolName); + } else if (isa<ObjCCategoryDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_ObjCCategoryName); + } else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_Namespace); + + // Part of the nested-name-specifier. + IsNestedNameSpecifier = true; + } + + return Contexts; +} + +void ASTUnit::CacheCodeCompletionResults() { + if (!TheSema) + return; + + SimpleTimer Timer(WantTiming); + Timer.setOutput("Cache global code completions for " + getMainFileName()); + + // Clear out the previous results. + ClearCachedCompletionResults(); + + // Gather the set of global code completions. + typedef CodeCompletionResult Result; + SmallVector<Result, 8> Results; + CachedCompletionAllocator = new GlobalCodeCompletionAllocator; + CodeCompletionTUInfo CCTUInfo(CachedCompletionAllocator); + TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, + CCTUInfo, Results); + + // Translate global code completions into cached completions. + llvm::DenseMap<CanQualType, unsigned> CompletionTypes; + + for (unsigned I = 0, N = Results.size(); I != N; ++I) { + switch (Results[I].Kind) { + case Result::RK_Declaration: { + bool IsNestedNameSpecifier = false; + CachedCodeCompletionResult CachedResult; + CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration, + Ctx->getLangOpts(), + IsNestedNameSpecifier); + CachedResult.Priority = Results[I].Priority; + CachedResult.Kind = Results[I].CursorKind; + CachedResult.Availability = Results[I].Availability; + + // Keep track of the type of this completion in an ASTContext-agnostic + // way. + QualType UsageType = getDeclUsageType(*Ctx, Results[I].Declaration); + if (UsageType.isNull()) { + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + } else { + CanQualType CanUsageType + = Ctx->getCanonicalType(UsageType.getUnqualifiedType()); + CachedResult.TypeClass = getSimplifiedTypeClass(CanUsageType); + + // Determine whether we have already seen this type. If so, we save + // ourselves the work of formatting the type string by using the + // temporary, CanQualType-based hash table to find the associated value. + unsigned &TypeValue = CompletionTypes[CanUsageType]; + if (TypeValue == 0) { + TypeValue = CompletionTypes.size(); + CachedCompletionTypes[QualType(CanUsageType).getAsString()] + = TypeValue; + } + + CachedResult.Type = TypeValue; + } + + CachedCompletionResults.push_back(CachedResult); + + /// Handle nested-name-specifiers in C++. + if (TheSema->Context.getLangOpts().CPlusPlus && + IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) { + // The contexts in which a nested-name-specifier can appear in C++. + uint64_t NNSContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_PotentiallyQualifiedName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); + + if (isa<NamespaceDecl>(Results[I].Declaration) || + isa<NamespaceAliasDecl>(Results[I].Declaration)) + NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace); + + if (unsigned RemainingContexts + = NNSContexts & ~CachedResult.ShowInContexts) { + // If there any contexts where this completion can be a + // nested-name-specifier but isn't already an option, create a + // nested-name-specifier completion. + Results[I].StartsNestedNameSpecifier = true; + CachedResult.Completion + = Results[I].CreateCodeCompletionString(*TheSema, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts = RemainingContexts; + CachedResult.Priority = CCP_NestedNameSpecifier; + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + CachedCompletionResults.push_back(CachedResult); + } + } + break; + } + + case Result::RK_Keyword: + case Result::RK_Pattern: + // Ignore keywords and patterns; we don't care, since they are so + // easily regenerated. + break; + + case Result::RK_Macro: { + CachedCodeCompletionResult CachedResult; + CachedResult.Completion + = Results[I].CreateCodeCompletionString(*TheSema, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_MacroNameUse) + | (1LL << CodeCompletionContext::CCC_PreprocessorExpression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_OtherWithMacros); + + CachedResult.Priority = Results[I].Priority; + CachedResult.Kind = Results[I].CursorKind; + CachedResult.Availability = Results[I].Availability; + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + CachedCompletionResults.push_back(CachedResult); + break; + } + } + } + + // Save the current top-level hash value. + CompletionCacheTopLevelHashValue = CurrentTopLevelHashValue; +} + +void ASTUnit::ClearCachedCompletionResults() { + CachedCompletionResults.clear(); + CachedCompletionTypes.clear(); + CachedCompletionAllocator = nullptr; +} + +namespace { + +/// \brief Gathers information from ASTReader that will be used to initialize +/// a Preprocessor. +class ASTInfoCollector : public ASTReaderListener { + Preprocessor &PP; + ASTContext &Context; + LangOptions &LangOpt; + std::shared_ptr<TargetOptions> &TargetOpts; + IntrusiveRefCntPtr<TargetInfo> &Target; + unsigned &Counter; + + bool InitializedLanguage; +public: + ASTInfoCollector(Preprocessor &PP, ASTContext &Context, LangOptions &LangOpt, + std::shared_ptr<TargetOptions> &TargetOpts, + IntrusiveRefCntPtr<TargetInfo> &Target, unsigned &Counter) + : PP(PP), Context(Context), LangOpt(LangOpt), TargetOpts(TargetOpts), + Target(Target), Counter(Counter), InitializedLanguage(false) {} + + bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain, + bool AllowCompatibleDifferences) override { + if (InitializedLanguage) + return false; + + LangOpt = LangOpts; + InitializedLanguage = true; + + updated(); + return false; + } + + bool ReadTargetOptions(const TargetOptions &TargetOpts, + bool Complain) override { + // If we've already initialized the target, don't do it again. + if (Target) + return false; + + this->TargetOpts = std::make_shared<TargetOptions>(TargetOpts); + Target = + TargetInfo::CreateTargetInfo(PP.getDiagnostics(), this->TargetOpts); + + updated(); + return false; + } + + void ReadCounter(const serialization::ModuleFile &M, + unsigned Value) override { + Counter = Value; + } + +private: + void updated() { + if (!Target || !InitializedLanguage) + return; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Target->adjust(LangOpt); + + // Initialize the preprocessor. + PP.Initialize(*Target); + + // Initialize the ASTContext + Context.InitBuiltinTypes(*Target); + + // We didn't have access to the comment options when the ASTContext was + // constructed, so register them now. + Context.getCommentCommandTraits().registerCommentOptions( + LangOpt.CommentOpts); + } +}; + + /// \brief Diagnostic consumer that saves each diagnostic it is given. +class StoredDiagnosticConsumer : public DiagnosticConsumer { + SmallVectorImpl<StoredDiagnostic> &StoredDiags; + SourceManager *SourceMgr; + +public: + explicit StoredDiagnosticConsumer( + SmallVectorImpl<StoredDiagnostic> &StoredDiags) + : StoredDiags(StoredDiags), SourceMgr(nullptr) {} + + void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = nullptr) override { + if (PP) + SourceMgr = &PP->getSourceManager(); + } + + void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) override; +}; + +/// \brief RAII object that optionally captures diagnostics, if +/// there is no diagnostic client to capture them already. +class CaptureDroppedDiagnostics { + DiagnosticsEngine &Diags; + StoredDiagnosticConsumer Client; + DiagnosticConsumer *PreviousClient; + std::unique_ptr<DiagnosticConsumer> OwningPreviousClient; + +public: + CaptureDroppedDiagnostics(bool RequestCapture, DiagnosticsEngine &Diags, + SmallVectorImpl<StoredDiagnostic> &StoredDiags) + : Diags(Diags), Client(StoredDiags), PreviousClient(nullptr) + { + if (RequestCapture || Diags.getClient() == nullptr) { + OwningPreviousClient = Diags.takeClient(); + PreviousClient = Diags.getClient(); + Diags.setClient(&Client, false); + } + } + + ~CaptureDroppedDiagnostics() { + if (Diags.getClient() == &Client) + Diags.setClient(PreviousClient, !!OwningPreviousClient.release()); + } +}; + +} // anonymous namespace + +void StoredDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Only record the diagnostic if it's part of the source manager we know + // about. This effectively drops diagnostics from modules we're building. + // FIXME: In the long run, ee don't want to drop source managers from modules. + if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr) + StoredDiags.push_back(StoredDiagnostic(Level, Info)); +} + +ASTMutationListener *ASTUnit::getASTMutationListener() { + if (WriterData) + return &WriterData->Writer; + return nullptr; +} + +ASTDeserializationListener *ASTUnit::getDeserializationListener() { + if (WriterData) + return &WriterData->Writer; + return nullptr; +} + +std::unique_ptr<llvm::MemoryBuffer> +ASTUnit::getBufferForFile(StringRef Filename, std::string *ErrorStr) { + assert(FileMgr); + auto Buffer = FileMgr->getBufferForFile(Filename); + if (Buffer) + return std::move(*Buffer); + if (ErrorStr) + *ErrorStr = Buffer.getError().message(); + return nullptr; +} + +/// \brief Configure the diagnostics object for use with ASTUnit. +void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + ASTUnit &AST, bool CaptureDiagnostics) { + assert(Diags.get() && "no DiagnosticsEngine was provided"); + if (CaptureDiagnostics) + Diags->setClient(new StoredDiagnosticConsumer(AST.StoredDiagnostics)); +} + +std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( + const std::string &Filename, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + const FileSystemOptions &FileSystemOpts, bool OnlyLocalDecls, + ArrayRef<RemappedFile> RemappedFiles, bool CaptureDiagnostics, + bool AllowPCHWithCompilerErrors, bool UserFilesAreVolatile) { + std::unique_ptr<ASTUnit> AST(new ASTUnit(true)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.get()); + + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->Diagnostics = Diags; + IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem(); + AST->FileMgr = new FileManager(FileSystemOpts, VFS); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), + AST->getFileManager(), + UserFilesAreVolatile); + AST->HSOpts = new HeaderSearchOptions(); + + AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts, + AST->getSourceManager(), + AST->getDiagnostics(), + AST->ASTFileLangOpts, + /*Target=*/nullptr)); + + PreprocessorOptions *PPOpts = new PreprocessorOptions(); + + for (unsigned I = 0, N = RemappedFiles.size(); I != N; ++I) + PPOpts->addRemappedFile(RemappedFiles[I].first, RemappedFiles[I].second); + + // Gather Info for preprocessor construction later on. + + HeaderSearch &HeaderInfo = *AST->HeaderInfo; + unsigned Counter; + + AST->PP = + new Preprocessor(PPOpts, AST->getDiagnostics(), AST->ASTFileLangOpts, + AST->getSourceManager(), HeaderInfo, *AST, + /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + Preprocessor &PP = *AST->PP; + + AST->Ctx = new ASTContext(AST->ASTFileLangOpts, AST->getSourceManager(), + PP.getIdentifierTable(), PP.getSelectorTable(), + PP.getBuiltinInfo()); + ASTContext &Context = *AST->Ctx; + + bool disableValid = false; + if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) + disableValid = true; + AST->Reader = new ASTReader(PP, Context, + /*isysroot=*/"", + /*DisableValidation=*/disableValid, + AllowPCHWithCompilerErrors); + + AST->Reader->setListener(llvm::make_unique<ASTInfoCollector>( + *AST->PP, Context, AST->ASTFileLangOpts, AST->TargetOpts, AST->Target, + Counter)); + + switch (AST->Reader->ReadAST(Filename, serialization::MK_MainFile, + SourceLocation(), ASTReader::ARR_None)) { + case ASTReader::Success: + break; + + case ASTReader::Failure: + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch); + return nullptr; + } + + AST->OriginalSourceFile = AST->Reader->getOriginalSourceFile(); + + PP.setCounterValue(Counter); + + // Attach the AST reader to the AST context as an external AST + // source, so that declarations will be deserialized from the + // AST file as needed. + Context.setExternalSource(AST->Reader); + + // Create an AST consumer, even though it isn't used. + AST->Consumer.reset(new ASTConsumer); + + // Create a semantic analysis object and tell the AST reader about it. + AST->TheSema.reset(new Sema(PP, Context, *AST->Consumer)); + AST->TheSema->Initialize(); + AST->Reader->InitializeSema(*AST->TheSema); + + // Tell the diagnostic client that we have started a source file. + AST->getDiagnostics().getClient()->BeginSourceFile(Context.getLangOpts(),&PP); + + return AST; +} + +namespace { + +/// \brief Preprocessor callback class that updates a hash value with the names +/// of all macros that have been defined by the translation unit. +class MacroDefinitionTrackerPPCallbacks : public PPCallbacks { + unsigned &Hash; + +public: + explicit MacroDefinitionTrackerPPCallbacks(unsigned &Hash) : Hash(Hash) { } + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash); + } +}; + +/// \brief Add the given declaration to the hash of all top-level entities. +void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { + if (!D) + return; + + DeclContext *DC = D->getDeclContext(); + if (!DC) + return; + + if (!(DC->isTranslationUnit() || DC->getLookupParent()->isTranslationUnit())) + return; + + if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + if (EnumDecl *EnumD = dyn_cast<EnumDecl>(D)) { + // For an unscoped enum include the enumerators in the hash since they + // enter the top-level namespace. + if (!EnumD->isScoped()) { + for (const auto *EI : EnumD->enumerators()) { + if (EI->getIdentifier()) + Hash = llvm::HashString(EI->getIdentifier()->getName(), Hash); + } + } + } + + if (ND->getIdentifier()) + Hash = llvm::HashString(ND->getIdentifier()->getName(), Hash); + else if (DeclarationName Name = ND->getDeclName()) { + std::string NameStr = Name.getAsString(); + Hash = llvm::HashString(NameStr, Hash); + } + return; + } + + if (ImportDecl *ImportD = dyn_cast<ImportDecl>(D)) { + if (Module *Mod = ImportD->getImportedModule()) { + std::string ModName = Mod->getFullModuleName(); + Hash = llvm::HashString(ModName, Hash); + } + return; + } +} + +class TopLevelDeclTrackerConsumer : public ASTConsumer { + ASTUnit &Unit; + unsigned &Hash; + +public: + TopLevelDeclTrackerConsumer(ASTUnit &_Unit, unsigned &Hash) + : Unit(_Unit), Hash(Hash) { + Hash = 0; + } + + void handleTopLevelDecl(Decl *D) { + if (!D) + return; + + // FIXME: Currently ObjC method declarations are incorrectly being + // reported as top-level declarations, even though their DeclContext + // is the containing ObjC @interface/@implementation. This is a + // fundamental problem in the parser right now. + if (isa<ObjCMethodDecl>(D)) + return; + + AddTopLevelDeclarationToHash(D, Hash); + Unit.addTopLevelDecl(D); + + handleFileLevelDecl(D); + } + + void handleFileLevelDecl(Decl *D) { + Unit.addFileLevelDecl(D); + if (NamespaceDecl *NSD = dyn_cast<NamespaceDecl>(D)) { + for (auto *I : NSD->decls()) + handleFileLevelDecl(I); + } + } + + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + return true; + } + + // We're not interested in "interesting" decls. + void HandleInterestingDecl(DeclGroupRef) override {} + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + } + + ASTMutationListener *GetASTMutationListener() override { + return Unit.getASTMutationListener(); + } + + ASTDeserializationListener *GetASTDeserializationListener() override { + return Unit.getDeserializationListener(); + } +}; + +class TopLevelDeclTrackerAction : public ASTFrontendAction { +public: + ASTUnit &Unit; + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique<MacroDefinitionTrackerPPCallbacks>( + Unit.getCurrentTopLevelHashValue())); + return llvm::make_unique<TopLevelDeclTrackerConsumer>( + Unit, Unit.getCurrentTopLevelHashValue()); + } + +public: + TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} + + bool hasCodeCompletionSupport() const override { return false; } + TranslationUnitKind getTranslationUnitKind() override { + return Unit.getTranslationUnitKind(); + } +}; + +class PrecompilePreambleAction : public ASTFrontendAction { + ASTUnit &Unit; + bool HasEmittedPreamblePCH; + +public: + explicit PrecompilePreambleAction(ASTUnit &Unit) + : Unit(Unit), HasEmittedPreamblePCH(false) {} + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; } + void setHasEmittedPreamblePCH() { HasEmittedPreamblePCH = true; } + bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); } + + bool hasCodeCompletionSupport() const override { return false; } + bool hasASTFileSupport() const override { return false; } + TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; } +}; + +class PrecompilePreambleConsumer : public PCHGenerator { + ASTUnit &Unit; + unsigned &Hash; + std::vector<Decl *> TopLevelDecls; + PrecompilePreambleAction *Action; + +public: + PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleAction *Action, + const Preprocessor &PP, StringRef isysroot, + raw_ostream *Out) + : PCHGenerator(PP, "", nullptr, isysroot, Out, /*AllowASTWithErrors=*/true), + Unit(Unit), Hash(Unit.getCurrentTopLevelHashValue()), Action(Action) { + Hash = 0; + } + + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { + Decl *D = *it; + // FIXME: Currently ObjC method declarations are incorrectly being + // reported as top-level declarations, even though their DeclContext + // is the containing ObjC @interface/@implementation. This is a + // fundamental problem in the parser right now. + if (isa<ObjCMethodDecl>(D)) + continue; + AddTopLevelDeclarationToHash(D, Hash); + TopLevelDecls.push_back(D); + } + return true; + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + PCHGenerator::HandleTranslationUnit(Ctx); + if (hasEmittedPCH()) { + // Translate the top-level declarations we captured during + // parsing into declaration IDs in the precompiled + // preamble. This will allow us to deserialize those top-level + // declarations when requested. + for (unsigned I = 0, N = TopLevelDecls.size(); I != N; ++I) { + Decl *D = TopLevelDecls[I]; + // Invalid top-level decls may not have been serialized. + if (D->isInvalidDecl()) + continue; + Unit.addTopLevelDeclFromPreamble(getWriter().getDeclID(D)); + } + + Action->setHasEmittedPreamblePCH(); + } + } +}; + +} + +std::unique_ptr<ASTConsumer> +PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = nullptr; + if (GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, + OutputFile, OS)) + return nullptr; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique<MacroDefinitionTrackerPPCallbacks>( + Unit.getCurrentTopLevelHashValue())); + return llvm::make_unique<PrecompilePreambleConsumer>( + Unit, this, CI.getPreprocessor(), Sysroot, OS); +} + +static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) { + return StoredDiag.getLocation().isValid(); +} + +static void +checkAndRemoveNonDriverDiags(SmallVectorImpl<StoredDiagnostic> &StoredDiags) { + // Get rid of stored diagnostics except the ones from the driver which do not + // have a source location. + StoredDiags.erase( + std::remove_if(StoredDiags.begin(), StoredDiags.end(), isNonDriverDiag), + StoredDiags.end()); +} + +static void checkAndSanitizeDiags(SmallVectorImpl<StoredDiagnostic> & + StoredDiagnostics, + SourceManager &SM) { + // The stored diagnostic has the old source manager in it; update + // the locations to refer into the new source manager. Since we've + // been careful to make sure that the source manager's state + // before and after are identical, so that we can reuse the source + // location itself. + for (unsigned I = 0, N = StoredDiagnostics.size(); I < N; ++I) { + if (StoredDiagnostics[I].getLocation().isValid()) { + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SM); + StoredDiagnostics[I].setLocation(Loc); + } + } +} + +/// Parse the source file into a translation unit using the given compiler +/// invocation, replacing the current translation unit. +/// +/// \returns True if a failure occurred that causes the ASTUnit not to +/// contain any translation-unit information, false otherwise. +bool ASTUnit::Parse(std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer) { + SavedMainFileBuffer.reset(); + + if (!Invocation) + return true; + + // Create the compiler instance to use for building the AST. + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + IntrusiveRefCntPtr<CompilerInvocation> + CCInvocation(new CompilerInvocation(*Invocation)); + + Clang->setInvocation(CCInvocation.get()); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return true; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang->getTarget().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + LangOpts = Clang->getInvocation().LangOpts; + FileSystemOpts = Clang->getFileSystemOpts(); + IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(Clang->getInvocation(), getDiagnostics()); + if (!VFS) + return true; + FileMgr = new FileManager(FileSystemOpts, VFS); + SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, + UserFilesAreVolatile); + TheSema.reset(); + Ctx = nullptr; + PP = nullptr; + Reader = nullptr; + + // Clear out old caches and data. + TopLevelDecls.clear(); + clearFileLevelDecls(); + CleanTemporaryFiles(); + + if (!OverrideMainBuffer) { + checkAndRemoveNonDriverDiags(StoredDiagnostics); + TopLevelDeclsInPreamble.clear(); + } + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(&getFileManager()); + + // Create the source manager. + Clang->setSourceManager(&getSourceManager()); + + // If the main file has been overridden due to the use of a preamble, + // make that override happen and introduce the preamble. + PreprocessorOptions &PreprocessorOpts = Clang->getPreprocessorOpts(); + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, + OverrideMainBuffer.get()); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); + PreprocessorOpts.DisablePCHValidation = true; + + // The stored diagnostic has the old source manager in it; update + // the locations to refer into the new source manager. Since we've + // been careful to make sure that the source manager's state + // before and after are identical, so that we can reuse the source + // location itself. + checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); + + // Keep track of the override buffer; + SavedMainFileBuffer = std::move(OverrideMainBuffer); + } + + std::unique_ptr<TopLevelDeclTrackerAction> Act( + new TopLevelDeclTrackerAction(*this)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> + ActCleanup(Act.get()); + + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) + goto error; + + if (SavedMainFileBuffer) { + std::string ModName = getPreambleFile(this); + TranslateStoredDiagnostics(getFileManager(), getSourceManager(), + PreambleDiagnostics, StoredDiagnostics); + } + + if (!Act->Execute()) + goto error; + + transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + FailedParseDiagnostics.clear(); + + return false; + +error: + // Remove the overridden buffer we used for the preamble. + SavedMainFileBuffer = nullptr; + + // Keep the ownership of the data in the ASTUnit because the client may + // want to see the diagnostics. + transferASTDataFromCompilerInstance(*Clang); + FailedParseDiagnostics.swap(StoredDiagnostics); + StoredDiagnostics.clear(); + NumStoredDiagnosticsFromDriver = 0; + return true; +} + +/// \brief Simple function to retrieve a path for a preamble precompiled header. +static std::string GetPreamblePCHPath() { + // FIXME: This is a hack so that we can override the preamble file during + // crash-recovery testing, which is the only case where the preamble files + // are not necessarily cleaned up. + const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"); + if (TmpFile) + return TmpFile; + + SmallString<128> Path; + llvm::sys::fs::createTemporaryFile("preamble", "pch", Path); + + return Path.str(); +} + +/// \brief Compute the preamble for the main file, providing the source buffer +/// that corresponds to the main file along with a pair (bytes, start-of-line) +/// that describes the preamble. +ASTUnit::ComputedPreamble +ASTUnit::ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines) { + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts = Invocation.getPreprocessorOpts(); + + // Try to determine if the main file has been remapped, either from the + // command line (to another file) or directly through the compiler invocation + // (to a memory buffer). + llvm::MemoryBuffer *Buffer = nullptr; + std::unique_ptr<llvm::MemoryBuffer> BufferOwner; + std::string MainFilePath(FrontendOpts.Inputs[0].getFile()); + llvm::sys::fs::UniqueID MainFileID; + if (!llvm::sys::fs::getUniqueID(MainFilePath, MainFileID)) { + // Check whether there is a file-file remapping of the main file + for (const auto &RF : PreprocessorOpts.RemappedFiles) { + std::string MPath(RF.first); + llvm::sys::fs::UniqueID MID; + if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + if (MainFileID == MID) { + // We found a remapping. Try to load the resulting, remapped source. + BufferOwner = getBufferForFile(RF.second); + if (!BufferOwner) + return ComputedPreamble(nullptr, nullptr, 0, true); + } + } + } + + // Check whether there is a file-buffer remapping. It supercedes the + // file-file remapping. + for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { + std::string MPath(RB.first); + llvm::sys::fs::UniqueID MID; + if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + if (MainFileID == MID) { + // We found a remapping. + BufferOwner.reset(); + Buffer = const_cast<llvm::MemoryBuffer *>(RB.second); + } + } + } + } + + // If the main source file was not remapped, load it now. + if (!Buffer && !BufferOwner) { + BufferOwner = getBufferForFile(FrontendOpts.Inputs[0].getFile()); + if (!BufferOwner) + return ComputedPreamble(nullptr, nullptr, 0, true); + } + + if (!Buffer) + Buffer = BufferOwner.get(); + auto Pre = Lexer::ComputePreamble(Buffer->getBuffer(), + *Invocation.getLangOpts(), MaxLines); + return ComputedPreamble(Buffer, std::move(BufferOwner), Pre.first, + Pre.second); +} + +ASTUnit::PreambleFileHash +ASTUnit::PreambleFileHash::createForFile(off_t Size, time_t ModTime) { + PreambleFileHash Result; + Result.Size = Size; + Result.ModTime = ModTime; + memset(Result.MD5, 0, sizeof(Result.MD5)); + return Result; +} + +ASTUnit::PreambleFileHash ASTUnit::PreambleFileHash::createForMemoryBuffer( + const llvm::MemoryBuffer *Buffer) { + PreambleFileHash Result; + Result.Size = Buffer->getBufferSize(); + Result.ModTime = 0; + + llvm::MD5 MD5Ctx; + MD5Ctx.update(Buffer->getBuffer().data()); + MD5Ctx.final(Result.MD5); + + return Result; +} + +namespace clang { +bool operator==(const ASTUnit::PreambleFileHash &LHS, + const ASTUnit::PreambleFileHash &RHS) { + return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime && + memcmp(LHS.MD5, RHS.MD5, sizeof(LHS.MD5)) == 0; +} +} // namespace clang + +static std::pair<unsigned, unsigned> +makeStandaloneRange(CharSourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts) { + CharSourceRange FileRange = Lexer::makeFileCharRange(Range, SM, LangOpts); + unsigned Offset = SM.getFileOffset(FileRange.getBegin()); + unsigned EndOffset = SM.getFileOffset(FileRange.getEnd()); + return std::make_pair(Offset, EndOffset); +} + +static ASTUnit::StandaloneFixIt makeStandaloneFixIt(const SourceManager &SM, + const LangOptions &LangOpts, + const FixItHint &InFix) { + ASTUnit::StandaloneFixIt OutFix; + OutFix.RemoveRange = makeStandaloneRange(InFix.RemoveRange, SM, LangOpts); + OutFix.InsertFromRange = makeStandaloneRange(InFix.InsertFromRange, SM, + LangOpts); + OutFix.CodeToInsert = InFix.CodeToInsert; + OutFix.BeforePreviousInsertions = InFix.BeforePreviousInsertions; + return OutFix; +} + +static ASTUnit::StandaloneDiagnostic +makeStandaloneDiagnostic(const LangOptions &LangOpts, + const StoredDiagnostic &InDiag) { + ASTUnit::StandaloneDiagnostic OutDiag; + OutDiag.ID = InDiag.getID(); + OutDiag.Level = InDiag.getLevel(); + OutDiag.Message = InDiag.getMessage(); + OutDiag.LocOffset = 0; + if (InDiag.getLocation().isInvalid()) + return OutDiag; + const SourceManager &SM = InDiag.getLocation().getManager(); + SourceLocation FileLoc = SM.getFileLoc(InDiag.getLocation()); + OutDiag.Filename = SM.getFilename(FileLoc); + if (OutDiag.Filename.empty()) + return OutDiag; + OutDiag.LocOffset = SM.getFileOffset(FileLoc); + for (StoredDiagnostic::range_iterator + I = InDiag.range_begin(), E = InDiag.range_end(); I != E; ++I) { + OutDiag.Ranges.push_back(makeStandaloneRange(*I, SM, LangOpts)); + } + for (StoredDiagnostic::fixit_iterator I = InDiag.fixit_begin(), + E = InDiag.fixit_end(); + I != E; ++I) + OutDiag.FixIts.push_back(makeStandaloneFixIt(SM, LangOpts, *I)); + + return OutDiag; +} + +/// \brief Attempt to build or re-use a precompiled preamble when (re-)parsing +/// the source file. +/// +/// This routine will compute the preamble of the main source file. If a +/// non-trivial preamble is found, it will precompile that preamble into a +/// precompiled header so that the precompiled preamble can be used to reduce +/// reparsing time. If a precompiled preamble has already been constructed, +/// this routine will determine if it is still valid and, if so, avoid +/// rebuilding the precompiled preamble. +/// +/// \param AllowRebuild When true (the default), this routine is +/// allowed to rebuild the precompiled preamble if it is found to be +/// out-of-date. +/// +/// \param MaxLines When non-zero, the maximum number of lines that +/// can occur within the preamble. +/// +/// \returns If the precompiled preamble can be used, returns a newly-allocated +/// buffer that should be used in place of the main file when doing so. +/// Otherwise, returns a NULL pointer. +std::unique_ptr<llvm::MemoryBuffer> +ASTUnit::getMainBufferWithPrecompiledPreamble( + const CompilerInvocation &PreambleInvocationIn, bool AllowRebuild, + unsigned MaxLines) { + + IntrusiveRefCntPtr<CompilerInvocation> + PreambleInvocation(new CompilerInvocation(PreambleInvocationIn)); + FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts + = PreambleInvocation->getPreprocessorOpts(); + + ComputedPreamble NewPreamble = ComputePreamble(*PreambleInvocation, MaxLines); + + if (!NewPreamble.Size) { + // We couldn't find a preamble in the main source. Clear out the current + // preamble, if we have one. It's obviously no good any more. + Preamble.clear(); + erasePreambleFile(this); + + // The next time we actually see a preamble, precompile it. + PreambleRebuildCounter = 1; + return nullptr; + } + + if (!Preamble.empty()) { + // We've previously computed a preamble. Check whether we have the same + // preamble now that we did before, and that there's enough space in + // the main-file buffer within the precompiled preamble to fit the + // new main file. + if (Preamble.size() == NewPreamble.Size && + PreambleEndsAtStartOfLine == NewPreamble.PreambleEndsAtStartOfLine && + memcmp(Preamble.getBufferStart(), NewPreamble.Buffer->getBufferStart(), + NewPreamble.Size) == 0) { + // The preamble has not changed. We may be able to re-use the precompiled + // preamble. + + // Check that none of the files used by the preamble have changed. + bool AnyFileChanged = false; + + // First, make a record of those files that have been overridden via + // remapping or unsaved_files. + llvm::StringMap<PreambleFileHash> OverriddenFiles; + for (const auto &R : PreprocessorOpts.RemappedFiles) { + if (AnyFileChanged) + break; + + vfs::Status Status; + if (FileMgr->getNoncachedStatValue(R.second, Status)) { + // If we can't stat the file we're remapping to, assume that something + // horrible happened. + AnyFileChanged = true; + break; + } + + OverriddenFiles[R.first] = PreambleFileHash::createForFile( + Status.getSize(), Status.getLastModificationTime().toEpochTime()); + } + + for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { + if (AnyFileChanged) + break; + OverriddenFiles[RB.first] = + PreambleFileHash::createForMemoryBuffer(RB.second); + } + + // Check whether anything has changed. + for (llvm::StringMap<PreambleFileHash>::iterator + F = FilesInPreamble.begin(), FEnd = FilesInPreamble.end(); + !AnyFileChanged && F != FEnd; + ++F) { + llvm::StringMap<PreambleFileHash>::iterator Overridden + = OverriddenFiles.find(F->first()); + if (Overridden != OverriddenFiles.end()) { + // This file was remapped; check whether the newly-mapped file + // matches up with the previous mapping. + if (Overridden->second != F->second) + AnyFileChanged = true; + continue; + } + + // The file was not remapped; check whether it has changed on disk. + vfs::Status Status; + if (FileMgr->getNoncachedStatValue(F->first(), Status)) { + // If we can't stat the file, assume that something horrible happened. + AnyFileChanged = true; + } else if (Status.getSize() != uint64_t(F->second.Size) || + Status.getLastModificationTime().toEpochTime() != + uint64_t(F->second.ModTime)) + AnyFileChanged = true; + } + + if (!AnyFileChanged) { + // Okay! We can re-use the precompiled preamble. + + // Set the state of the diagnostic object to mimic its state + // after parsing the preamble. + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), + PreambleInvocation->getDiagnosticOpts()); + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + return llvm::MemoryBuffer::getMemBufferCopy( + NewPreamble.Buffer->getBuffer(), FrontendOpts.Inputs[0].getFile()); + } + } + + // If we aren't allowed to rebuild the precompiled preamble, just + // return now. + if (!AllowRebuild) + return nullptr; + + // We can't reuse the previously-computed preamble. Build a new one. + Preamble.clear(); + PreambleDiagnostics.clear(); + erasePreambleFile(this); + PreambleRebuildCounter = 1; + } else if (!AllowRebuild) { + // We aren't allowed to rebuild the precompiled preamble; just + // return now. + return nullptr; + } + + // If the preamble rebuild counter > 1, it's because we previously + // failed to build a preamble and we're not yet ready to try + // again. Decrement the counter and return a failure. + if (PreambleRebuildCounter > 1) { + --PreambleRebuildCounter; + return nullptr; + } + + // Create a temporary file for the precompiled preamble. In rare + // circumstances, this can fail. + std::string PreamblePCHPath = GetPreamblePCHPath(); + if (PreamblePCHPath.empty()) { + // Try again next time. + PreambleRebuildCounter = 1; + return nullptr; + } + + // We did not previously compute a preamble, or it can't be reused anyway. + SimpleTimer PreambleTimer(WantTiming); + PreambleTimer.setOutput("Precompiling preamble"); + + // Save the preamble text for later; we'll need to compare against it for + // subsequent reparses. + StringRef MainFilename = FrontendOpts.Inputs[0].getFile(); + Preamble.assign(FileMgr->getFile(MainFilename), + NewPreamble.Buffer->getBufferStart(), + NewPreamble.Buffer->getBufferStart() + NewPreamble.Size); + PreambleEndsAtStartOfLine = NewPreamble.PreambleEndsAtStartOfLine; + + PreambleBuffer = llvm::MemoryBuffer::getMemBufferCopy( + NewPreamble.Buffer->getBuffer().slice(0, Preamble.size()), MainFilename); + + // Remap the main source file to the preamble buffer. + StringRef MainFilePath = FrontendOpts.Inputs[0].getFile(); + PreprocessorOpts.addRemappedFile(MainFilePath, PreambleBuffer.get()); + + // Tell the compiler invocation to generate a temporary precompiled header. + FrontendOpts.ProgramAction = frontend::GeneratePCH; + // FIXME: Generate the precompiled header into memory? + FrontendOpts.OutputFile = PreamblePCHPath; + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + + // Create the compiler instance to use for building the precompiled preamble. + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(&*PreambleInvocation); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing all of the diagnostics produced. + Clang->setDiagnostics(&getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) { + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.RemappedFileBuffers.pop_back(); + return nullptr; + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang->getTarget().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not support here!"); + + // Clear out old caches and data. + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Clang->getDiagnosticOpts()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); + TopLevelDecls.clear(); + TopLevelDeclsInPreamble.clear(); + PreambleDiagnostics.clear(); + + IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(Clang->getInvocation(), getDiagnostics()); + if (!VFS) + return nullptr; + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS)); + + // Create the source manager. + Clang->setSourceManager(new SourceManager(getDiagnostics(), + Clang->getFileManager())); + + auto PreambleDepCollector = std::make_shared<DependencyCollector>(); + Clang->addDependencyCollector(PreambleDepCollector); + + std::unique_ptr<PrecompilePreambleAction> Act; + Act.reset(new PrecompilePreambleAction(*this)); + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.RemappedFileBuffers.pop_back(); + return nullptr; + } + + Act->Execute(); + + // Transfer any diagnostics generated when parsing the preamble into the set + // of preamble diagnostics. + for (stored_diag_iterator I = stored_diag_afterDriver_begin(), + E = stored_diag_end(); + I != E; ++I) + PreambleDiagnostics.push_back( + makeStandaloneDiagnostic(Clang->getLangOpts(), *I)); + + Act->EndSourceFile(); + + checkAndRemoveNonDriverDiags(StoredDiagnostics); + + if (!Act->hasEmittedPreamblePCH()) { + // The preamble PCH failed (e.g. there was a module loading fatal error), + // so no precompiled header was generated. Forget that we even tried. + // FIXME: Should we leave a note for ourselves to try again? + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + TopLevelDeclsInPreamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.RemappedFileBuffers.pop_back(); + return nullptr; + } + + // Keep track of the preamble we precompiled. + setPreambleFile(this, FrontendOpts.OutputFile); + NumWarningsInPreamble = getDiagnostics().getNumWarnings(); + + // Keep track of all of the files that the source manager knows about, + // so we can verify whether they have changed or not. + FilesInPreamble.clear(); + SourceManager &SourceMgr = Clang->getSourceManager(); + for (auto &Filename : PreambleDepCollector->getDependencies()) { + const FileEntry *File = Clang->getFileManager().getFile(Filename); + if (!File || File == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())) + continue; + if (time_t ModTime = File->getModificationTime()) { + FilesInPreamble[File->getName()] = PreambleFileHash::createForFile( + File->getSize(), ModTime); + } else { + llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File); + FilesInPreamble[File->getName()] = + PreambleFileHash::createForMemoryBuffer(Buffer); + } + } + + PreambleRebuildCounter = 1; + PreprocessorOpts.RemappedFileBuffers.pop_back(); + + // If the hash of top-level entities differs from the hash of the top-level + // entities the last time we rebuilt the preamble, clear out the completion + // cache. + if (CurrentTopLevelHashValue != PreambleTopLevelHashValue) { + CompletionCacheTopLevelHashValue = 0; + PreambleTopLevelHashValue = CurrentTopLevelHashValue; + } + + return llvm::MemoryBuffer::getMemBufferCopy(NewPreamble.Buffer->getBuffer(), + MainFilename); +} + +void ASTUnit::RealizeTopLevelDeclsFromPreamble() { + std::vector<Decl *> Resolved; + Resolved.reserve(TopLevelDeclsInPreamble.size()); + ExternalASTSource &Source = *getASTContext().getExternalSource(); + for (unsigned I = 0, N = TopLevelDeclsInPreamble.size(); I != N; ++I) { + // Resolve the declaration ID to an actual declaration, possibly + // deserializing the declaration in the process. + Decl *D = Source.GetExternalDecl(TopLevelDeclsInPreamble[I]); + if (D) + Resolved.push_back(D); + } + TopLevelDeclsInPreamble.clear(); + TopLevelDecls.insert(TopLevelDecls.begin(), Resolved.begin(), Resolved.end()); +} + +void ASTUnit::transferASTDataFromCompilerInstance(CompilerInstance &CI) { + // Steal the created target, context, and preprocessor if they have been + // created. + assert(CI.hasInvocation() && "missing invocation"); + LangOpts = CI.getInvocation().LangOpts; + TheSema = CI.takeSema(); + Consumer = CI.takeASTConsumer(); + if (CI.hasASTContext()) + Ctx = &CI.getASTContext(); + if (CI.hasPreprocessor()) + PP = &CI.getPreprocessor(); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + if (CI.hasTarget()) + Target = &CI.getTarget(); + Reader = CI.getModuleManager(); + HadModuleLoaderFatalFailure = CI.hadModuleLoaderFatalFailure(); +} + +StringRef ASTUnit::getMainFileName() const { + if (Invocation && !Invocation->getFrontendOpts().Inputs.empty()) { + const FrontendInputFile &Input = Invocation->getFrontendOpts().Inputs[0]; + if (Input.isFile()) + return Input.getFile(); + else + return Input.getBuffer()->getBufferIdentifier(); + } + + if (SourceMgr) { + if (const FileEntry * + FE = SourceMgr->getFileEntryForID(SourceMgr->getMainFileID())) + return FE->getName(); + } + + return StringRef(); +} + +StringRef ASTUnit::getASTFileName() const { + if (!isMainFileAST()) + return StringRef(); + + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Mod.FileName; +} + +ASTUnit *ASTUnit::create(CompilerInvocation *CI, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool CaptureDiagnostics, + bool UserFilesAreVolatile) { + std::unique_ptr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->Invocation = CI; + AST->FileSystemOpts = CI->getFileSystemOpts(); + IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(*CI, *Diags); + if (!VFS) + return nullptr; + AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr, + UserFilesAreVolatile); + + return AST.release(); +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( + CompilerInvocation *CI, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + ASTFrontendAction *Action, ASTUnit *Unit, bool Persistent, + StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, + bool PrecompilePreamble, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, bool UserFilesAreVolatile, + std::unique_ptr<ASTUnit> *ErrAST) { + assert(CI && "A CompilerInvocation is required"); + + std::unique_ptr<ASTUnit> OwnAST; + ASTUnit *AST = Unit; + if (!AST) { + // Create the AST unit. + OwnAST.reset(create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile)); + AST = OwnAST.get(); + if (!AST) + return nullptr; + } + + if (!ResourceFilesPath.empty()) { + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + } + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + if (PrecompilePreamble) + AST->PreambleRebuildCounter = 2; + AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(OwnAST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.get()); + + // We'll manage file buffers ourselves. + CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; + CI->getFrontendOpts().DisableFree = false; + ProcessWarningOptions(AST->getDiagnostics(), CI->getDiagnosticOpts()); + + // Create the compiler instance to use for building the AST. + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(CI); + AST->OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&AST->getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return nullptr; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang->getTarget().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not supported here!"); + + // Configure the various subsystems. + AST->TheSema.reset(); + AST->Ctx = nullptr; + AST->PP = nullptr; + AST->Reader = nullptr; + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(&AST->getFileManager()); + + // Create the source manager. + Clang->setSourceManager(&AST->getSourceManager()); + + ASTFrontendAction *Act = Action; + + std::unique_ptr<TopLevelDeclTrackerAction> TrackerAct; + if (!Act) { + TrackerAct.reset(new TopLevelDeclTrackerAction(*AST)); + Act = TrackerAct.get(); + } + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> + ActCleanup(TrackerAct.get()); + + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return nullptr; + } + + if (Persistent && !TrackerAct) { + Clang->getPreprocessor().addPPCallbacks( + llvm::make_unique<MacroDefinitionTrackerPPCallbacks>( + AST->getCurrentTopLevelHashValue())); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + if (Clang->hasASTConsumer()) + Consumers.push_back(Clang->takeASTConsumer()); + Consumers.push_back(llvm::make_unique<TopLevelDeclTrackerConsumer>( + *AST, AST->getCurrentTopLevelHashValue())); + Clang->setASTConsumer( + llvm::make_unique<MultiplexConsumer>(std::move(Consumers))); + } + if (!Act->Execute()) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return nullptr; + } + + // Steal the created target, context, and preprocessor. + AST->transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + if (OwnAST) + return OwnAST.release(); + else + return AST; +} + +bool ASTUnit::LoadFromCompilerInvocation(bool PrecompilePreamble) { + if (!Invocation) + return true; + + // We'll manage file buffers ourselves. + Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; + Invocation->getFrontendOpts().DisableFree = false; + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (PrecompilePreamble) { + PreambleRebuildCounter = 2; + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(*Invocation); + } + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Parsing " + getMainFileName()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<llvm::MemoryBuffer> + MemBufferCleanup(OverrideMainBuffer.get()); + + return Parse(std::move(OverrideMainBuffer)); +} + +std::unique_ptr<ASTUnit> ASTUnit::LoadFromCompilerInvocation( + CompilerInvocation *CI, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool OnlyLocalDecls, bool CaptureDiagnostics, bool PrecompilePreamble, + TranslationUnitKind TUKind, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, bool UserFilesAreVolatile) { + // Create the AST unit. + std::unique_ptr<ASTUnit> AST(new ASTUnit(false)); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->Invocation = CI; + AST->FileSystemOpts = CI->getFileSystemOpts(); + IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(*CI, *Diags); + if (!VFS) + return nullptr; + AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.get()); + + if (AST->LoadFromCompilerInvocation(PrecompilePreamble)) + return nullptr; + return AST; +} + +ASTUnit *ASTUnit::LoadFromCommandLine( + const char **ArgBegin, const char **ArgEnd, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, StringRef ResourceFilesPath, + bool OnlyLocalDecls, bool CaptureDiagnostics, + ArrayRef<RemappedFile> RemappedFiles, bool RemappedFilesKeepOriginalName, + bool PrecompilePreamble, TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, + bool AllowPCHWithCompilerErrors, bool SkipFunctionBodies, + bool UserFilesAreVolatile, bool ForSerialization, + std::unique_ptr<ASTUnit> *ErrAST) { + assert(Diags.get() && "no DiagnosticsEngine was provided"); + + SmallVector<StoredDiagnostic, 4> StoredDiagnostics; + + IntrusiveRefCntPtr<CompilerInvocation> CI; + + { + + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, + StoredDiagnostics); + + CI = clang::createInvocationFromCommandLine( + llvm::makeArrayRef(ArgBegin, ArgEnd), + Diags); + if (!CI) + return nullptr; + } + + // Override any files that need remapping + for (unsigned I = 0, N = RemappedFiles.size(); I != N; ++I) { + CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + } + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); + PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; + PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; + + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + + CI->getFrontendOpts().SkipFunctionBodies = SkipFunctionBodies; + + // Create the AST unit. + std::unique_ptr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->FileSystemOpts = CI->getFileSystemOpts(); + IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(*CI, *Diags); + if (!VFS) + return nullptr; + AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); + AST->StoredDiagnostics.swap(StoredDiagnostics); + AST->Invocation = CI; + if (ForSerialization) + AST->WriterData.reset(new ASTWriterData()); + // Zero out now to ease cleanup during crash recovery. + CI = nullptr; + Diags = nullptr; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + + if (AST->LoadFromCompilerInvocation(PrecompilePreamble)) { + // Some error occurred, if caller wants to examine diagnostics, pass it the + // ASTUnit. + if (ErrAST) { + AST->StoredDiagnostics.swap(AST->FailedParseDiagnostics); + ErrAST->swap(AST); + } + return nullptr; + } + + return AST.release(); +} + +bool ASTUnit::Reparse(ArrayRef<RemappedFile> RemappedFiles) { + if (!Invocation) + return true; + + clearFileLevelDecls(); + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Reparsing " + getMainFileName()); + + // Remap files. + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (const auto &RB : PPOpts.RemappedFileBuffers) + delete RB.second; + + Invocation->getPreprocessorOpts().clearRemappedFiles(); + for (unsigned I = 0, N = RemappedFiles.size(); I != N; ++I) { + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + } + + // If we have a preamble file lying around, or if we might try to + // build a precompiled preamble, do so now. + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(*Invocation); + + // Clear out the diagnostics state. + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + if (OverrideMainBuffer) + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + // Parse the sources + bool Result = Parse(std::move(OverrideMainBuffer)); + + // If we're caching global code-completion results, and the top-level + // declarations have changed, clear out the code-completion cache. + if (!Result && ShouldCacheCodeCompletionResults && + CurrentTopLevelHashValue != CompletionCacheTopLevelHashValue) + CacheCodeCompletionResults(); + + // We now need to clear out the completion info related to this translation + // unit; it'll be recreated if necessary. + CCTUInfo.reset(); + + return Result; +} + +//----------------------------------------------------------------------------// +// Code completion +//----------------------------------------------------------------------------// + +namespace { + /// \brief Code completion consumer that combines the cached code-completion + /// results from an ASTUnit with the code-completion results provided to it, + /// then passes the result on to + class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer { + uint64_t NormalContexts; + ASTUnit &AST; + CodeCompleteConsumer &Next; + + public: + AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next, + const CodeCompleteOptions &CodeCompleteOpts) + : CodeCompleteConsumer(CodeCompleteOpts, Next.isOutputBinary()), + AST(AST), Next(Next) + { + // Compute the set of contexts in which we will look when we don't have + // any information about the specific context. + NormalContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_DotMemberAccess) + | (1LL << CodeCompletionContext::CCC_ArrowMemberAccess) + | (1LL << CodeCompletionContext::CCC_ObjCPropertyAccess) + | (1LL << CodeCompletionContext::CCC_ObjCProtocolName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_Recovery); + + if (AST.getASTContext().getLangOpts().CPlusPlus) + NormalContexts |= (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag); + } + + void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) override; + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates) override { + Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates); + } + + CodeCompletionAllocator &getAllocator() override { + return Next.getAllocator(); + } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { + return Next.getCodeCompletionTUInfo(); + } + }; +} + +/// \brief Helper function that computes which global names are hidden by the +/// local code-completion results. +static void CalculateHiddenNames(const CodeCompletionContext &Context, + CodeCompletionResult *Results, + unsigned NumResults, + ASTContext &Ctx, + llvm::StringSet<llvm::BumpPtrAllocator> &HiddenNames){ + bool OnlyTagNames = false; + switch (Context.getKind()) { + case CodeCompletionContext::CCC_Recovery: + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Statement: + case CodeCompletionContext::CCC_Expression: + case CodeCompletionContext::CCC_ObjCMessageReceiver: + case CodeCompletionContext::CCC_DotMemberAccess: + case CodeCompletionContext::CCC_ArrowMemberAccess: + case CodeCompletionContext::CCC_ObjCPropertyAccess: + case CodeCompletionContext::CCC_Namespace: + case CodeCompletionContext::CCC_Type: + case CodeCompletionContext::CCC_Name: + case CodeCompletionContext::CCC_PotentiallyQualifiedName: + case CodeCompletionContext::CCC_ParenthesizedExpression: + case CodeCompletionContext::CCC_ObjCInterfaceName: + break; + + case CodeCompletionContext::CCC_EnumTag: + case CodeCompletionContext::CCC_UnionTag: + case CodeCompletionContext::CCC_ClassOrStructTag: + OnlyTagNames = true; + break; + + case CodeCompletionContext::CCC_ObjCProtocolName: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_MacroNameUse: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_NaturalLanguage: + case CodeCompletionContext::CCC_SelectorName: + case CodeCompletionContext::CCC_TypeQualifiers: + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_OtherWithMacros: + case CodeCompletionContext::CCC_ObjCInstanceMessage: + case CodeCompletionContext::CCC_ObjCClassMessage: + case CodeCompletionContext::CCC_ObjCCategoryName: + // We're looking for nothing, or we're looking for names that cannot + // be hidden. + return; + } + + typedef CodeCompletionResult Result; + for (unsigned I = 0; I != NumResults; ++I) { + if (Results[I].Kind != Result::RK_Declaration) + continue; + + unsigned IDNS + = Results[I].Declaration->getUnderlyingDecl()->getIdentifierNamespace(); + + bool Hiding = false; + if (OnlyTagNames) + Hiding = (IDNS & Decl::IDNS_Tag); + else { + unsigned HiddenIDNS = (Decl::IDNS_Type | Decl::IDNS_Member | + Decl::IDNS_Namespace | Decl::IDNS_Ordinary | + Decl::IDNS_NonMemberOperator); + if (Ctx.getLangOpts().CPlusPlus) + HiddenIDNS |= Decl::IDNS_Tag; + Hiding = (IDNS & HiddenIDNS); + } + + if (!Hiding) + continue; + + DeclarationName Name = Results[I].Declaration->getDeclName(); + if (IdentifierInfo *Identifier = Name.getAsIdentifierInfo()) + HiddenNames.insert(Identifier->getName()); + else + HiddenNames.insert(Name.getAsString()); + } +} + + +void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) { + // Merge the results we were given with the results we cached. + bool AddedResult = false; + uint64_t InContexts = + Context.getKind() == CodeCompletionContext::CCC_Recovery + ? NormalContexts : (1LL << Context.getKind()); + // Contains the set of names that are hidden by "local" completion results. + llvm::StringSet<llvm::BumpPtrAllocator> HiddenNames; + typedef CodeCompletionResult Result; + SmallVector<Result, 8> AllResults; + for (ASTUnit::cached_completion_iterator + C = AST.cached_completion_begin(), + CEnd = AST.cached_completion_end(); + C != CEnd; ++C) { + // If the context we are in matches any of the contexts we are + // interested in, we'll add this result. + if ((C->ShowInContexts & InContexts) == 0) + continue; + + // If we haven't added any results previously, do so now. + if (!AddedResult) { + CalculateHiddenNames(Context, Results, NumResults, S.Context, + HiddenNames); + AllResults.insert(AllResults.end(), Results, Results + NumResults); + AddedResult = true; + } + + // Determine whether this global completion result is hidden by a local + // completion result. If so, skip it. + if (C->Kind != CXCursor_MacroDefinition && + HiddenNames.count(C->Completion->getTypedText())) + continue; + + // Adjust priority based on similar type classes. + unsigned Priority = C->Priority; + CodeCompletionString *Completion = C->Completion; + if (!Context.getPreferredType().isNull()) { + if (C->Kind == CXCursor_MacroDefinition) { + Priority = getMacroUsagePriority(C->Completion->getTypedText(), + S.getLangOpts(), + Context.getPreferredType()->isAnyPointerType()); + } else if (C->Type) { + CanQualType Expected + = S.Context.getCanonicalType( + Context.getPreferredType().getUnqualifiedType()); + SimplifiedTypeClass ExpectedSTC = getSimplifiedTypeClass(Expected); + if (ExpectedSTC == C->TypeClass) { + // We know this type is similar; check for an exact match. + llvm::StringMap<unsigned> &CachedCompletionTypes + = AST.getCachedCompletionTypes(); + llvm::StringMap<unsigned>::iterator Pos + = CachedCompletionTypes.find(QualType(Expected).getAsString()); + if (Pos != CachedCompletionTypes.end() && Pos->second == C->Type) + Priority /= CCF_ExactTypeMatch; + else + Priority /= CCF_SimilarTypeMatch; + } + } + } + + // Adjust the completion string, if required. + if (C->Kind == CXCursor_MacroDefinition && + Context.getKind() == CodeCompletionContext::CCC_MacroNameUse) { + // Create a new code-completion string that just contains the + // macro name, without its arguments. + CodeCompletionBuilder Builder(getAllocator(), getCodeCompletionTUInfo(), + CCP_CodePattern, C->Availability); + Builder.AddTypedTextChunk(C->Completion->getTypedText()); + Priority = CCP_CodePattern; + Completion = Builder.TakeString(); + } + + AllResults.push_back(Result(Completion, Priority, C->Kind, + C->Availability)); + } + + // If we did not add any cached completion results, just forward the + // results we were given to the next consumer. + if (!AddedResult) { + Next.ProcessCodeCompleteResults(S, Context, Results, NumResults); + return; + } + + Next.ProcessCodeCompleteResults(S, Context, AllResults.data(), + AllResults.size()); +} + + + +void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, + ArrayRef<RemappedFile> RemappedFiles, + bool IncludeMacros, + bool IncludeCodePatterns, + bool IncludeBriefComments, + CodeCompleteConsumer &Consumer, + DiagnosticsEngine &Diag, LangOptions &LangOpts, + SourceManager &SourceMgr, FileManager &FileMgr, + SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics, + SmallVectorImpl<const llvm::MemoryBuffer *> &OwnedBuffers) { + if (!Invocation) + return; + + SimpleTimer CompletionTimer(WantTiming); + CompletionTimer.setOutput("Code completion @ " + File + ":" + + Twine(Line) + ":" + Twine(Column)); + + IntrusiveRefCntPtr<CompilerInvocation> + CCInvocation(new CompilerInvocation(*Invocation)); + + FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); + CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts; + PreprocessorOptions &PreprocessorOpts = CCInvocation->getPreprocessorOpts(); + + CodeCompleteOpts.IncludeMacros = IncludeMacros && + CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns; + CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments; + + assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); + + FrontendOpts.CodeCompletionAt.FileName = File; + FrontendOpts.CodeCompletionAt.Line = Line; + FrontendOpts.CodeCompletionAt.Column = Column; + + // Set the language options appropriately. + LangOpts = *CCInvocation->getLangOpts(); + + // Spell-checking and warnings are wasteful during code-completion. + LangOpts.SpellChecking = false; + CCInvocation->getDiagnosticOpts().IgnoreWarnings = true; + + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(&*CCInvocation); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics produced. + Clang->setDiagnostics(&Diag); + CaptureDroppedDiagnostics Capture(true, + Clang->getDiagnostics(), + StoredDiagnostics); + ProcessWarningOptions(Diag, CCInvocation->getDiagnosticOpts()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) { + Clang->setInvocation(nullptr); + return; + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang->getTarget().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not support here!"); + + + // Use the source and file managers that we were given. + Clang->setFileManager(&FileMgr); + Clang->setSourceManager(&SourceMgr); + + // Remap files. + PreprocessorOpts.clearRemappedFiles(); + PreprocessorOpts.RetainRemappedFileBuffers = true; + for (unsigned I = 0, N = RemappedFiles.size(); I != N; ++I) { + PreprocessorOpts.addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + OwnedBuffers.push_back(RemappedFiles[I].second); + } + + // Use the code completion consumer we were given, but adding any cached + // code-completion results. + AugmentedCodeCompleteConsumer *AugmentedConsumer + = new AugmentedCodeCompleteConsumer(*this, Consumer, CodeCompleteOpts); + Clang->setCodeCompletionConsumer(AugmentedConsumer); + + // If we have a precompiled preamble, try to use it. We only allow + // the use of the precompiled preamble if we're if the completion + // point is within the main file, after the end of the precompiled + // preamble. + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (!getPreambleFile(this).empty()) { + std::string CompleteFilePath(File); + llvm::sys::fs::UniqueID CompleteFileID; + + if (!llvm::sys::fs::getUniqueID(CompleteFilePath, CompleteFileID)) { + std::string MainPath(OriginalSourceFile); + llvm::sys::fs::UniqueID MainID; + if (!llvm::sys::fs::getUniqueID(MainPath, MainID)) { + if (CompleteFileID == MainID && Line > 1) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble( + *CCInvocation, false, Line - 1); + } + } + } + + // If the main file has been overridden due to the use of a preamble, + // make that override happen and introduce the preamble. + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, + OverrideMainBuffer.get()); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); + PreprocessorOpts.DisablePCHValidation = true; + + OwnedBuffers.push_back(OverrideMainBuffer.release()); + } else { + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + } + + // Disable the preprocessing record if modules are not enabled. + if (!Clang->getLangOpts().Modules) + PreprocessorOpts.DetailedRecord = false; + + std::unique_ptr<SyntaxOnlyAction> Act; + Act.reset(new SyntaxOnlyAction); + if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + Act->Execute(); + Act->EndSourceFile(); + } +} + +bool ASTUnit::Save(StringRef File) { + if (HadModuleLoaderFatalFailure) + return true; + + // Write to a temporary file and later rename it to the actual file, to avoid + // possible race conditions. + SmallString<128> TempPath; + TempPath = File; + TempPath += "-%%%%%%%%"; + int fd; + if (llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath)) + return true; + + // FIXME: Can we somehow regenerate the stat cache here, or do we need to + // unconditionally create a stat cache when we parse the file? + llvm::raw_fd_ostream Out(fd, /*shouldClose=*/true); + + serialize(Out); + Out.close(); + if (Out.has_error()) { + Out.clear_error(); + return true; + } + + if (llvm::sys::fs::rename(TempPath.str(), File)) { + llvm::sys::fs::remove(TempPath.str()); + return true; + } + + return false; +} + +static bool serializeUnit(ASTWriter &Writer, + SmallVectorImpl<char> &Buffer, + Sema &S, + bool hasErrors, + raw_ostream &OS) { + Writer.WriteAST(S, std::string(), nullptr, "", hasErrors); + + // Write the generated bitstream to "Out". + if (!Buffer.empty()) + OS.write(Buffer.data(), Buffer.size()); + + return false; +} + +bool ASTUnit::serialize(raw_ostream &OS) { + bool hasErrors = getDiagnostics().hasErrorOccurred(); + + if (WriterData) + return serializeUnit(WriterData->Writer, WriterData->Buffer, + getSema(), hasErrors, OS); + + SmallString<128> Buffer; + llvm::BitstreamWriter Stream(Buffer); + ASTWriter Writer(Stream); + return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS); +} + +typedef ContinuousRangeMap<unsigned, int, 2> SLocRemap; + +void ASTUnit::TranslateStoredDiagnostics( + FileManager &FileMgr, + SourceManager &SrcMgr, + const SmallVectorImpl<StandaloneDiagnostic> &Diags, + SmallVectorImpl<StoredDiagnostic> &Out) { + // Map the standalone diagnostic into the new source manager. We also need to + // remap all the locations to the new view. This includes the diag location, + // any associated source ranges, and the source ranges of associated fix-its. + // FIXME: There should be a cleaner way to do this. + + SmallVector<StoredDiagnostic, 4> Result; + Result.reserve(Diags.size()); + for (unsigned I = 0, N = Diags.size(); I != N; ++I) { + // Rebuild the StoredDiagnostic. + const StandaloneDiagnostic &SD = Diags[I]; + if (SD.Filename.empty()) + continue; + const FileEntry *FE = FileMgr.getFile(SD.Filename); + if (!FE) + continue; + FileID FID = SrcMgr.translateFile(FE); + SourceLocation FileLoc = SrcMgr.getLocForStartOfFile(FID); + if (FileLoc.isInvalid()) + continue; + SourceLocation L = FileLoc.getLocWithOffset(SD.LocOffset); + FullSourceLoc Loc(L, SrcMgr); + + SmallVector<CharSourceRange, 4> Ranges; + Ranges.reserve(SD.Ranges.size()); + for (std::vector<std::pair<unsigned, unsigned> >::const_iterator + I = SD.Ranges.begin(), E = SD.Ranges.end(); I != E; ++I) { + SourceLocation BL = FileLoc.getLocWithOffset((*I).first); + SourceLocation EL = FileLoc.getLocWithOffset((*I).second); + Ranges.push_back(CharSourceRange::getCharRange(BL, EL)); + } + + SmallVector<FixItHint, 2> FixIts; + FixIts.reserve(SD.FixIts.size()); + for (std::vector<StandaloneFixIt>::const_iterator + I = SD.FixIts.begin(), E = SD.FixIts.end(); + I != E; ++I) { + FixIts.push_back(FixItHint()); + FixItHint &FH = FixIts.back(); + FH.CodeToInsert = I->CodeToInsert; + SourceLocation BL = FileLoc.getLocWithOffset(I->RemoveRange.first); + SourceLocation EL = FileLoc.getLocWithOffset(I->RemoveRange.second); + FH.RemoveRange = CharSourceRange::getCharRange(BL, EL); + } + + Result.push_back(StoredDiagnostic(SD.Level, SD.ID, + SD.Message, Loc, Ranges, FixIts)); + } + Result.swap(Out); +} + +void ASTUnit::addFileLevelDecl(Decl *D) { + assert(D); + + // We only care about local declarations. + if (D->isFromASTFile()) + return; + + SourceManager &SM = *SourceMgr; + SourceLocation Loc = D->getLocation(); + if (Loc.isInvalid() || !SM.isLocalSourceLocation(Loc)) + return; + + // We only keep track of the file-level declarations of each file. + if (!D->getLexicalDeclContext()->isFileContext()) + return; + + SourceLocation FileLoc = SM.getFileLoc(Loc); + assert(SM.isLocalSourceLocation(FileLoc)); + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(FileLoc); + if (FID.isInvalid()) + return; + + LocDeclsTy *&Decls = FileDecls[FID]; + if (!Decls) + Decls = new LocDeclsTy(); + + std::pair<unsigned, Decl *> LocDecl(Offset, D); + + if (Decls->empty() || Decls->back().first <= Offset) { + Decls->push_back(LocDecl); + return; + } + + LocDeclsTy::iterator I = std::upper_bound(Decls->begin(), Decls->end(), + LocDecl, llvm::less_first()); + + Decls->insert(I, LocDecl); +} + +void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, + SmallVectorImpl<Decl *> &Decls) { + if (File.isInvalid()) + return; + + if (SourceMgr->isLoadedFileID(File)) { + assert(Ctx->getExternalSource() && "No external source!"); + return Ctx->getExternalSource()->FindFileRegionDecls(File, Offset, Length, + Decls); + } + + FileDeclsTy::iterator I = FileDecls.find(File); + if (I == FileDecls.end()) + return; + + LocDeclsTy &LocDecls = *I->second; + if (LocDecls.empty()) + return; + + LocDeclsTy::iterator BeginIt = + std::lower_bound(LocDecls.begin(), LocDecls.end(), + std::make_pair(Offset, (Decl *)nullptr), + llvm::less_first()); + if (BeginIt != LocDecls.begin()) + --BeginIt; + + // If we are pointing at a top-level decl inside an objc container, we need + // to backtrack until we find it otherwise we will fail to report that the + // region overlaps with an objc container. + while (BeginIt != LocDecls.begin() && + BeginIt->second->isTopLevelDeclInObjCContainer()) + --BeginIt; + + LocDeclsTy::iterator EndIt = std::upper_bound( + LocDecls.begin(), LocDecls.end(), + std::make_pair(Offset + Length, (Decl *)nullptr), llvm::less_first()); + if (EndIt != LocDecls.end()) + ++EndIt; + + for (LocDeclsTy::iterator DIt = BeginIt; DIt != EndIt; ++DIt) + Decls.push_back(DIt->second); +} + +SourceLocation ASTUnit::getLocation(const FileEntry *File, + unsigned Line, unsigned Col) const { + const SourceManager &SM = getSourceManager(); + SourceLocation Loc = SM.translateFileLineCol(File, Line, Col); + return SM.getMacroArgExpandedLocation(Loc); +} + +SourceLocation ASTUnit::getLocation(const FileEntry *File, + unsigned Offset) const { + const SourceManager &SM = getSourceManager(); + SourceLocation FileLoc = SM.translateFileLineCol(File, 1, 1); + return SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Offset)); +} + +/// \brief If \arg Loc is a loaded location from the preamble, returns +/// the corresponding local location of the main file, otherwise it returns +/// \arg Loc. +SourceLocation ASTUnit::mapLocationFromPreamble(SourceLocation Loc) { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble.size()) { + SourceLocation FileLoc + = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +/// \brief If \arg Loc is a local location of the main file but inside the +/// preamble chunk, returns the corresponding loaded location from the +/// preamble, otherwise it returns \arg Loc. +SourceLocation ASTUnit::mapLocationToPreamble(SourceLocation Loc) { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, SourceMgr->getMainFileID(), &Offs) && + Offs < Preamble.size()) { + SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +bool ASTUnit::isInPreambleFileID(SourceLocation Loc) { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +bool ASTUnit::isInMainFileID(SourceLocation Loc) { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +SourceLocation ASTUnit::getEndOfPreambleFileID() { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (FID.isInvalid()) + return SourceLocation(); + + return SourceMgr->getLocForEndOfFile(FID); +} + +SourceLocation ASTUnit::getStartOfMainFileID() { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (FID.isInvalid()) + return SourceLocation(); + + return SourceMgr->getLocForStartOfFile(FID); +} + +std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> +ASTUnit::getLocalPreprocessingEntities() const { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Reader->getModulePreprocessedEntities(Mod); + } + + if (PreprocessingRecord *PPRec = PP->getPreprocessingRecord()) + return std::make_pair(PPRec->local_begin(), PPRec->local_end()); + + return std::make_pair(PreprocessingRecord::iterator(), + PreprocessingRecord::iterator()); +} + +bool ASTUnit::visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn) { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + ASTReader::ModuleDeclIterator MDI, MDE; + std::tie(MDI, MDE) = Reader->getModuleFileLevelDecls(Mod); + for (; MDI != MDE; ++MDI) { + if (!Fn(context, *MDI)) + return false; + } + + return true; + } + + for (ASTUnit::top_level_iterator TL = top_level_begin(), + TLEnd = top_level_end(); + TL != TLEnd; ++TL) { + if (!Fn(context, *TL)) + return false; + } + + return true; +} + +namespace { +struct PCHLocatorInfo { + serialization::ModuleFile *Mod; + PCHLocatorInfo() : Mod(nullptr) {} +}; +} + +static bool PCHLocator(serialization::ModuleFile &M, void *UserData) { + PCHLocatorInfo &Info = *static_cast<PCHLocatorInfo*>(UserData); + switch (M.Kind) { + case serialization::MK_ImplicitModule: + case serialization::MK_ExplicitModule: + return true; // skip dependencies. + case serialization::MK_PCH: + Info.Mod = &M; + return true; // found it. + case serialization::MK_Preamble: + return false; // look in dependencies. + case serialization::MK_MainFile: + return false; // look in dependencies. + } + + return true; +} + +const FileEntry *ASTUnit::getPCHFile() { + if (!Reader) + return nullptr; + + PCHLocatorInfo Info; + Reader->getModuleManager().visit(PCHLocator, &Info); + if (Info.Mod) + return Info.Mod->File; + + return nullptr; +} + +bool ASTUnit::isModuleFile() { + return isMainFileAST() && !ASTFileLangOpts.CurrentModule.empty(); +} + +void ASTUnit::PreambleData::countLines() const { + NumLines = 0; + if (empty()) + return; + + for (std::vector<char>::const_iterator + I = Buffer.begin(), E = Buffer.end(); I != E; ++I) { + if (*I == '\n') + ++NumLines; + } + if (Buffer.back() != '\n') + ++NumLines; +} + +#ifndef NDEBUG +ASTUnit::ConcurrencyState::ConcurrencyState() { + Mutex = new llvm::sys::MutexImpl(/*recursive=*/true); +} + +ASTUnit::ConcurrencyState::~ConcurrencyState() { + delete static_cast<llvm::sys::MutexImpl *>(Mutex); +} + +void ASTUnit::ConcurrencyState::start() { + bool acquired = static_cast<llvm::sys::MutexImpl *>(Mutex)->tryacquire(); + assert(acquired && "Concurrent access to ASTUnit!"); +} + +void ASTUnit::ConcurrencyState::finish() { + static_cast<llvm::sys::MutexImpl *>(Mutex)->release(); +} + +#else // NDEBUG + +ASTUnit::ConcurrencyState::ConcurrencyState() { Mutex = 0; } +ASTUnit::ConcurrencyState::~ConcurrencyState() {} +void ASTUnit::ConcurrencyState::start() {} +void ASTUnit::ConcurrencyState::finish() {} + +#endif diff --git a/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp new file mode 100644 index 0000000..d909d52 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp @@ -0,0 +1,684 @@ +//===--- 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/Frontend/Utils.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemStatCache.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/OnDiskHashTable.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +// FIXME: put this somewhere else? +#ifndef S_ISDIR +#define S_ISDIR(x) (((x)&_S_IFDIR)!=0) +#endif + +using namespace clang; + +//===----------------------------------------------------------------------===// +// PTH-specific stuff. +//===----------------------------------------------------------------------===// + +typedef uint32_t Offset; + +namespace { +class 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 PTHEntryKeyVariant { + union { const FileEntry* FE; const char* Path; }; + enum { IsFE = 0x1, IsDE = 0x2, IsNoExist = 0x0 } Kind; + FileData *Data; + +public: + PTHEntryKeyVariant(const FileEntry *fe) : FE(fe), Kind(IsFE), Data(nullptr) {} + + PTHEntryKeyVariant(FileData *Data, const char *path) + : Path(path), Kind(IsDE), Data(new FileData(*Data)) {} + + explicit PTHEntryKeyVariant(const char *path) + : Path(path), Kind(IsNoExist), Data(nullptr) {} + + bool isFile() const { return Kind == IsFE; } + + StringRef getString() const { + return Kind == IsFE ? FE->getName() : Path; + } + + unsigned getKind() const { return (unsigned) Kind; } + + void EmitData(raw_ostream& Out) { + using namespace llvm::support; + endian::Writer<little> LE(Out); + switch (Kind) { + case IsFE: { + // Emit stat information. + llvm::sys::fs::UniqueID UID = FE->getUniqueID(); + LE.write<uint64_t>(UID.getFile()); + LE.write<uint64_t>(UID.getDevice()); + LE.write<uint64_t>(FE->getModificationTime()); + LE.write<uint64_t>(FE->getSize()); + } break; + case IsDE: + // Emit stat information. + LE.write<uint64_t>(Data->UniqueID.getFile()); + LE.write<uint64_t>(Data->UniqueID.getDevice()); + LE.write<uint64_t>(Data->ModTime); + LE.write<uint64_t>(Data->Size); + delete Data; + break; + default: + break; + } + } + + unsigned getRepresentationLength() const { + return Kind == IsNoExist ? 0 : 4 + 4 + 2 + 8 + 8; + } +}; + +class FileEntryPTHEntryInfo { +public: + typedef PTHEntryKeyVariant key_type; + typedef key_type key_type_ref; + + typedef PTHEntry data_type; + typedef const PTHEntry& data_type_ref; + + typedef unsigned hash_value_type; + typedef unsigned offset_type; + + static hash_value_type ComputeHash(PTHEntryKeyVariant V) { + return llvm::HashString(V.getString()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E) { + using namespace llvm::support; + endian::Writer<little> LE(Out); + + unsigned n = V.getString().size() + 1 + 1; + LE.write<uint16_t>(n); + + unsigned m = V.getRepresentationLength() + (V.isFile() ? 4 + 4 : 0); + LE.write<uint8_t>(m); + + return std::make_pair(n, m); + } + + static void EmitKey(raw_ostream& Out, PTHEntryKeyVariant V, unsigned n){ + using namespace llvm::support; + // Emit the entry kind. + endian::Writer<little>(Out).write<uint8_t>((unsigned)V.getKind()); + // Emit the string. + Out.write(V.getString().data(), n - 1); + } + + static void EmitData(raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E, unsigned) { + using namespace llvm::support; + endian::Writer<little> LE(Out); + + // For file entries emit the offsets into the PTH file for token data + // and the preprocessor blocks table. + if (V.isFile()) { + LE.write<uint32_t>(E.getTokenOffset()); + LE.write<uint32_t>(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 llvm::OnDiskChainedHashTableGenerator<FileEntryPTHEntryInfo> PTHMap; + +namespace { +class PTHWriter { + typedef llvm::DenseMap<const IdentifierInfo*,uint32_t> IDMap; + typedef llvm::StringMap<OffsetOpt, llvm::BumpPtrAllocator> CachedStrsTy; + + 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) { + using namespace llvm::support; + endian::Writer<little>(Out).write<uint8_t>(V); + } + + void Emit16(uint32_t V) { + using namespace llvm::support; + endian::Writer<little>(Out).write<uint16_t>(V); + } + + void Emit32(uint32_t V) { + using namespace llvm::support; + endian::Writer<little>(Out).write<uint32_t>(V); + } + + void EmitBuf(const char *Ptr, unsigned NumBytes) { + Out.write(Ptr, NumBytes); + } + + void EmitString(StringRef V) { + using namespace llvm::support; + endian::Writer<little>(Out).write<uint16_t>(V.size()); + EmitBuf(V.data(), V.size()); + } + + /// 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(); + +public: + PTHWriter(llvm::raw_fd_ostream& out, Preprocessor& pp) + : Out(out), PP(pp), idcount(0), CurStrOffset(0) {} + + PTHMap &getPM() { return PM; } + void GeneratePTH(const std::string &MainFile); +}; +} // 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()) + return I->second; // We've already added 1. + + IM[II] = ++idcount; // Pre-increment since '0' is reserved for NULL. + return idcount; +} + +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()) { + Emit32(ResolveID(T.getIdentifierInfo())); + } else { + // We cache *un-cleaned* spellings. This gives us 100% fidelity with the + // source code. + StringRef s(T.getLiteralData(), T.getLength()); + + // Get the string entry. + auto &E = *CachedStrs.insert(std::make_pair(s, OffsetOpt())).first; + + // If this is a new string entry, bump the PTH offset. + if (!E.second.hasOffset()) { + E.second.setOffset(CurStrOffset); + StrEntries.push_back(&E); + CurStrOffset += s.size() + 1; + } + + // Emit the relative offset into the PTH file for the spelling string. + Emit32(E.second.getOffset()); + } + + // 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. + using namespace llvm::support; + endian::Writer<little> LE(Out); + uint32_t TokenOff = Out.tell(); + for (uint64_t N = llvm::OffsetToAlignment(TokenOff, 4); N; --N, ++TokenOff) + LE.write<uint8_t>(0); + + // 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 eod 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::eod); + Tmp.clearFlag(Token::StartOfLine); + Tmp.setIdentifierInfo(nullptr); + EmitToken(Tmp); + ParsingPreprocessorDirective = false; + } + + if (Tok.is(tok::raw_identifier)) { + 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(); + + // Get the next token. + Token NextTok; + L.LexFromRawLexer(NextTok); + + // If we see the start of line, then we had a null directive "#". In + // this case, discard both tokens. + if (NextTok.isAtStartOfLine()) + goto NextToken; + + // The token is the start of a directive. Emit it. + EmitToken(Tok); + Tok = NextTok; + + // Did we see 'include'/'import'/'include_next'? + if (Tok.isNot(tok::raw_identifier)) { + EmitToken(Tok); + continue; + } + + IdentifierInfo* II = PP.LookUpIdentifierInfo(Tok); + tok::PPKeywordKind K = II->getPPKeywordID(); + + ParsingPreprocessorDirective = true; + + switch (K) { + case tok::pp_not_keyword: + // Invalid directives "#foo" can occur in #if 0 blocks etc, just pass + // them through. + 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::raw_identifier)) + 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.isNot(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 - TokenOff); + 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(TokenOff, 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) + EmitBuf((*I)->getKeyData(), (*I)->getKeyLength()+1 /*nul included*/); + + return SpellingsOff; +} + +void PTHWriter::GeneratePTH(const std::string &MainFile) { + // Generate the prologue. + Out << "cfe-pth" << '\0'; + Emit32(PTHManager::Version); + + // Leave 4 words for the prologue. + Offset PrologueOffset = Out.tell(); + for (unsigned i = 0; i < 4; ++i) + Emit32(0); + + // Write the name of the MainFile. + if (!MainFile.empty()) { + EmitString(MainFile); + } 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.getLangOpts(); + + 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.OrigEntry; + + // FIXME: Handle files with non-absolute paths. + if (llvm::sys::path::is_relative(FE->getName())) + continue; + + const llvm::MemoryBuffer *B = C.getBuffer(PP.getDiagnostics(), SM); + if (!B) continue; + + FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer L(FID, FromFile, 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); +} + +namespace { +/// 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 FileSystemStatCache { + PTHMap &PM; +public: + StatListener(PTHMap &pm) : PM(pm) {} + ~StatListener() {} + + LookupResult getStat(const char *Path, FileData &Data, bool isFile, + std::unique_ptr<vfs::File> *F, + vfs::FileSystem &FS) override { + LookupResult Result = statChained(Path, Data, isFile, F, FS); + + if (Result == CacheMissing) // Failed 'stat'. + PM.insert(PTHEntryKeyVariant(Path), PTHEntry()); + else if (Data.IsDirectory) { + // Only cache directories with absolute paths. + if (llvm::sys::path::is_relative(Path)) + return Result; + + PM.insert(PTHEntryKeyVariant(&Data, Path), PTHEntry()); + } + + return Result; + } +}; +} // end anonymous namespace + + +void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) { + // Get the name of the main file. + const SourceManager &SrcMgr = PP.getSourceManager(); + const FileEntry *MainFile = SrcMgr.getFileEntryForID(SrcMgr.getMainFileID()); + SmallString<128> MainFilePath(MainFile->getName()); + + llvm::sys::fs::make_absolute(MainFilePath); + + // Create the PTHWriter. + PTHWriter PW(*OS, PP); + + // Install the 'stat' system call listener in the FileManager. + auto StatCacheOwner = llvm::make_unique<StatListener>(PW.getPM()); + StatListener *StatCache = StatCacheOwner.get(); + PP.getFileManager().addStatCache(std::move(StatCacheOwner), + /*AtBeginning=*/true); + + // 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().removeStatCache(StatCache); + PW.GeneratePTH(MainFilePath.str()); +} + +//===----------------------------------------------------------------------===// + +namespace { +class PTHIdKey { +public: + const IdentifierInfo* II; + uint32_t FileOffset; +}; + +class PTHIdentifierTableTrait { +public: + typedef PTHIdKey* key_type; + typedef key_type key_type_ref; + + typedef uint32_t data_type; + typedef data_type data_type_ref; + + typedef unsigned hash_value_type; + typedef unsigned offset_type; + + static hash_value_type ComputeHash(PTHIdKey* key) { + return llvm::HashString(key->II->getName()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(raw_ostream& Out, const PTHIdKey* key, uint32_t) { + using namespace llvm::support; + unsigned n = key->II->getLength() + 1; + endian::Writer<little>(Out).write<uint16_t>(n); + return std::make_pair(n, sizeof(uint32_t)); + } + + static void EmitKey(raw_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->getNameStart(), n); + } + + static void EmitData(raw_ostream& Out, PTHIdKey*, uint32_t pID, + unsigned) { + using namespace llvm::support; + endian::Writer<little>(Out).write<uint32_t>(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. + llvm::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/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp new file mode 100644 index 0000000..d77fd18 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp @@ -0,0 +1,14 @@ +//===- ChainedDiagnosticConsumer.cpp - Chain Diagnostic Clients -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedDiagnosticConsumer.h" + +using namespace clang; + +void ChainedDiagnosticConsumer::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp new file mode 100644 index 0000000..cb260b4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -0,0 +1,287 @@ +//===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- 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 ChainedIncludesSource class, which converts headers +// to chained PCHs in memory, mainly used for testing. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; + +namespace { +class ChainedIncludesSource : public ExternalSemaSource { +public: + virtual ~ChainedIncludesSource(); + + ExternalSemaSource &getFinalReader() const { return *FinalReader; } + + std::vector<CompilerInstance *> CIs; + IntrusiveRefCntPtr<ExternalSemaSource> FinalReader; + +protected: + //===----------------------------------------------------------------------===// + // ExternalASTSource interface. + //===----------------------------------------------------------------------===// + + Decl *GetExternalDecl(uint32_t ID) override; + Selector GetExternalSelector(uint32_t ID) override; + uint32_t GetNumExternalSelectors() override; + Stmt *GetExternalDeclStmt(uint64_t Offset) override; + CXXBaseSpecifier *GetExternalCXXBaseSpecifiers(uint64_t Offset) override; + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override; + ExternalLoadResult + FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), + SmallVectorImpl<Decl *> &Result) override; + void CompleteType(TagDecl *Tag) override; + void CompleteType(ObjCInterfaceDecl *Class) override; + void StartedDeserializing() override; + void FinishedDeserializing() override; + void StartTranslationUnit(ASTConsumer *Consumer) override; + void PrintStats() override; + + /// Return the amount of memory used by memory buffers, breaking down + /// by heap-backed versus mmap'ed memory. + void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override; + + //===----------------------------------------------------------------------===// + // ExternalSemaSource interface. + //===----------------------------------------------------------------------===// + + void InitializeSema(Sema &S) override; + void ForgetSema() override; + void ReadMethodPool(Selector Sel) override; + bool LookupUnqualified(LookupResult &R, Scope *S) override; +}; +} + +static ASTReader * +createASTReader(CompilerInstance &CI, StringRef pchFile, + SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &MemBufs, + SmallVectorImpl<std::string> &bufNames, + ASTDeserializationListener *deserialListener = nullptr) { + Preprocessor &PP = CI.getPreprocessor(); + std::unique_ptr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, CI.getASTContext(), /*isysroot=*/"", + /*DisableValidation=*/true)); + for (unsigned ti = 0; ti < bufNames.size(); ++ti) { + StringRef sr(bufNames[ti]); + Reader->addInMemoryBuffer(sr, std::move(MemBufs[ti])); + } + Reader->setDeserializationListener(deserialListener); + switch (Reader->ReadAST(pchFile, serialization::MK_PCH, SourceLocation(), + ASTReader::ARR_None)) { + case ASTReader::Success: + // Set the predefines buffer as suggested by the PCH reader. + PP.setPredefines(Reader->getSuggestedPredefines()); + return Reader.release(); + + case ASTReader::Failure: + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + break; + } + return nullptr; +} + +ChainedIncludesSource::~ChainedIncludesSource() { + for (unsigned i = 0, e = CIs.size(); i != e; ++i) + delete CIs[i]; +} + +IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource( + CompilerInstance &CI, IntrusiveRefCntPtr<ExternalSemaSource> &Reader) { + + std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; + assert(!includes.empty() && "No '-chain-include' in options!"); + + IntrusiveRefCntPtr<ChainedIncludesSource> source(new ChainedIncludesSource()); + InputKind IK = CI.getFrontendOpts().Inputs[0].getKind(); + + SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs; + SmallVector<std::string, 4> serialBufNames; + + for (unsigned i = 0, e = includes.size(); i != e; ++i) { + bool firstInclude = (i == 0); + std::unique_ptr<CompilerInvocation> CInvok; + CInvok.reset(new CompilerInvocation(CI.getInvocation())); + + CInvok->getPreprocessorOpts().ChainedIncludes.clear(); + CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); + CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear(); + CInvok->getPreprocessorOpts().DisablePCHValidation = true; + CInvok->getPreprocessorOpts().Includes.clear(); + CInvok->getPreprocessorOpts().MacroIncludes.clear(); + CInvok->getPreprocessorOpts().Macros.clear(); + + CInvok->getFrontendOpts().Inputs.clear(); + FrontendInputFile InputFile(includes[i], IK); + CInvok->getFrontendOpts().Inputs.push_back(InputFile); + + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), DiagClient)); + + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + Clang->setInvocation(CInvok.release()); + Clang->setDiagnostics(Diags.get()); + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + Clang->createFileManager(); + Clang->createSourceManager(Clang->getFileManager()); + Clang->createPreprocessor(TU_Prefix); + Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(), + &Clang->getPreprocessor()); + Clang->createASTContext(); + + SmallVector<char, 256> serialAST; + llvm::raw_svector_ostream OS(serialAST); + auto consumer = + llvm::make_unique<PCHGenerator>(Clang->getPreprocessor(), "-", nullptr, + /*isysroot=*/"", &OS); + Clang->getASTContext().setASTMutationListener( + consumer->GetASTMutationListener()); + Clang->setASTConsumer(std::move(consumer)); + Clang->createSema(TU_Prefix, nullptr); + + if (firstInclude) { + Preprocessor &PP = Clang->getPreprocessor(); + PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + assert(!SerialBufs.empty()); + SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs; + // TODO: Pass through the existing MemoryBuffer instances instead of + // allocating new ones. + for (auto &SB : SerialBufs) + Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer())); + std::string pchName = includes[i-1]; + llvm::raw_string_ostream os(pchName); + os << ".pch" << i-1; + serialBufNames.push_back(os.str()); + + IntrusiveRefCntPtr<ASTReader> Reader; + Reader = createASTReader( + *Clang, pchName, Bufs, serialBufNames, + Clang->getASTConsumer().GetASTDeserializationListener()); + if (!Reader) + return nullptr; + Clang->setModuleManager(Reader); + Clang->getASTContext().setExternalSource(Reader); + } + + if (!Clang->InitializeSourceManager(InputFile)) + return nullptr; + + ParseAST(Clang->getSema()); + Clang->getDiagnosticClient().EndSourceFile(); + SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy(OS.str())); + source->CIs.push_back(Clang.release()); + } + + assert(!SerialBufs.empty()); + std::string pchName = includes.back() + ".pch-final"; + serialBufNames.push_back(pchName); + Reader = createASTReader(CI, pchName, SerialBufs, serialBufNames); + if (!Reader) + return nullptr; + + source->FinalReader = Reader; + return source; +} + +//===----------------------------------------------------------------------===// +// ExternalASTSource interface. +//===----------------------------------------------------------------------===// + +Decl *ChainedIncludesSource::GetExternalDecl(uint32_t ID) { + return getFinalReader().GetExternalDecl(ID); +} +Selector ChainedIncludesSource::GetExternalSelector(uint32_t ID) { + return getFinalReader().GetExternalSelector(ID); +} +uint32_t ChainedIncludesSource::GetNumExternalSelectors() { + return getFinalReader().GetNumExternalSelectors(); +} +Stmt *ChainedIncludesSource::GetExternalDeclStmt(uint64_t Offset) { + return getFinalReader().GetExternalDeclStmt(Offset); +} +CXXBaseSpecifier * +ChainedIncludesSource::GetExternalCXXBaseSpecifiers(uint64_t Offset) { + return getFinalReader().GetExternalCXXBaseSpecifiers(Offset); +} +bool +ChainedIncludesSource::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + return getFinalReader().FindExternalVisibleDeclsByName(DC, Name); +} +ExternalLoadResult +ChainedIncludesSource::FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), + SmallVectorImpl<Decl*> &Result) { + return getFinalReader().FindExternalLexicalDecls(DC, isKindWeWant, Result); +} +void ChainedIncludesSource::CompleteType(TagDecl *Tag) { + return getFinalReader().CompleteType(Tag); +} +void ChainedIncludesSource::CompleteType(ObjCInterfaceDecl *Class) { + return getFinalReader().CompleteType(Class); +} +void ChainedIncludesSource::StartedDeserializing() { + return getFinalReader().StartedDeserializing(); +} +void ChainedIncludesSource::FinishedDeserializing() { + return getFinalReader().FinishedDeserializing(); +} +void ChainedIncludesSource::StartTranslationUnit(ASTConsumer *Consumer) { + return getFinalReader().StartTranslationUnit(Consumer); +} +void ChainedIncludesSource::PrintStats() { + return getFinalReader().PrintStats(); +} +void ChainedIncludesSource::getMemoryBufferSizes(MemoryBufferSizes &sizes)const{ + for (unsigned i = 0, e = CIs.size(); i != e; ++i) { + if (const ExternalASTSource *eSrc = + CIs[i]->getASTContext().getExternalSource()) { + eSrc->getMemoryBufferSizes(sizes); + } + } + + getFinalReader().getMemoryBufferSizes(sizes); +} + +void ChainedIncludesSource::InitializeSema(Sema &S) { + return getFinalReader().InitializeSema(S); +} +void ChainedIncludesSource::ForgetSema() { + return getFinalReader().ForgetSema(); +} +void ChainedIncludesSource::ReadMethodPool(Selector Sel) { + getFinalReader().ReadMethodPool(Sel); +} +bool ChainedIncludesSource::LookupUnqualified(LookupResult &R, Scope *S) { + return getFinalReader().LookupUnqualified(R, S); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/CodeGenOptions.cpp b/contrib/llvm/tools/clang/lib/Frontend/CodeGenOptions.cpp new file mode 100644 index 0000000..75ee47f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CodeGenOptions.cpp @@ -0,0 +1,24 @@ +//===--- CodeGenOptions.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CodeGenOptions.h" +#include <string.h> + +namespace clang { + +CodeGenOptions::CodeGenOptions() { +#define CODEGENOPT(Name, Bits, Default) Name = Default; +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) set##Name(Default); +#include "clang/Frontend/CodeGenOptions.def" + + RelocationModel = "pic"; + memcpy(CoverageVersion, "402*", 4); +} + +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp new file mode 100644 index 0000000..93a34b7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -0,0 +1,1706 @@ +//===--- CompilerInstance.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" +#include "clang/Frontend/ChainedDiagnosticConsumer.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PTHManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/GlobalModuleIndex.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/LockFileManager.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <sys/stat.h> +#include <system_error> +#include <time.h> + +using namespace clang; + +CompilerInstance::CompilerInstance(bool BuildingModule) + : ModuleLoader(BuildingModule), + Invocation(new CompilerInvocation()), ModuleManager(nullptr), + BuildGlobalModuleIndex(false), HaveFullGlobalModuleIndex(false), + ModuleBuildFailed(false) { +} + +CompilerInstance::~CompilerInstance() { + assert(OutputFiles.empty() && "Still output files in flight?"); +} + +void CompilerInstance::setInvocation(CompilerInvocation *Value) { + Invocation = Value; +} + +bool CompilerInstance::shouldBuildGlobalModuleIndex() const { + return (BuildGlobalModuleIndex || + (ModuleManager && ModuleManager->isGlobalIndexUnavailable() && + getFrontendOpts().GenerateGlobalModuleIndex)) && + !ModuleBuildFailed; +} + +void CompilerInstance::setDiagnostics(DiagnosticsEngine *Value) { + Diagnostics = Value; +} + +void CompilerInstance::setTarget(TargetInfo *Value) { + Target = Value; +} + +void CompilerInstance::setFileManager(FileManager *Value) { + FileMgr = Value; + if (Value) + VirtualFileSystem = Value->getVirtualFileSystem(); + else + VirtualFileSystem.reset(); +} + +void CompilerInstance::setSourceManager(SourceManager *Value) { + SourceMgr = Value; +} + +void CompilerInstance::setPreprocessor(Preprocessor *Value) { PP = Value; } + +void CompilerInstance::setASTContext(ASTContext *Value) { Context = Value; } + +void CompilerInstance::setSema(Sema *S) { + TheSema.reset(S); +} + +void CompilerInstance::setASTConsumer(std::unique_ptr<ASTConsumer> Value) { + Consumer = std::move(Value); +} + +void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { + CompletionConsumer.reset(Value); +} + +std::unique_ptr<Sema> CompilerInstance::takeSema() { + return std::move(TheSema); +} + +IntrusiveRefCntPtr<ASTReader> CompilerInstance::getModuleManager() const { + return ModuleManager; +} +void CompilerInstance::setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader) { + ModuleManager = Reader; +} + +std::shared_ptr<ModuleDependencyCollector> +CompilerInstance::getModuleDepCollector() const { + return ModuleDepCollector; +} + +void CompilerInstance::setModuleDepCollector( + std::shared_ptr<ModuleDependencyCollector> Collector) { + ModuleDepCollector = Collector; +} + +// Diagnostics +static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts, + const CodeGenOptions *CodeGenOpts, + DiagnosticsEngine &Diags) { + std::error_code EC; + std::unique_ptr<raw_ostream> StreamOwner; + raw_ostream *OS = &llvm::errs(); + if (DiagOpts->DiagnosticLogFile != "-") { + // Create the output stream. + auto FileOS = llvm::make_unique<llvm::raw_fd_ostream>( + DiagOpts->DiagnosticLogFile, EC, + llvm::sys::fs::F_Append | llvm::sys::fs::F_Text); + if (EC) { + Diags.Report(diag::warn_fe_cc_log_diagnostics_failure) + << DiagOpts->DiagnosticLogFile << EC.message(); + } else { + FileOS->SetUnbuffered(); + FileOS->SetUseAtomicWrites(true); + OS = FileOS.get(); + StreamOwner = std::move(FileOS); + } + } + + // Chain in the diagnostic client which will log the diagnostics. + auto Logger = llvm::make_unique<LogDiagnosticPrinter>(*OS, DiagOpts, + std::move(StreamOwner)); + if (CodeGenOpts) + Logger->setDwarfDebugFlags(CodeGenOpts->DwarfDebugFlags); + assert(Diags.ownsClient()); + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.takeClient(), std::move(Logger))); +} + +static void SetupSerializedDiagnostics(DiagnosticOptions *DiagOpts, + DiagnosticsEngine &Diags, + StringRef OutputFile) { + auto SerializedConsumer = + clang::serialized_diags::create(OutputFile, DiagOpts); + + if (Diags.ownsClient()) { + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.takeClient(), std::move(SerializedConsumer))); + } else { + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.getClient(), std::move(SerializedConsumer))); + } +} + +void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client, + bool ShouldOwnClient) { + Diagnostics = createDiagnostics(&getDiagnosticOpts(), Client, + ShouldOwnClient, &getCodeGenOpts()); +} + +IntrusiveRefCntPtr<DiagnosticsEngine> +CompilerInstance::createDiagnostics(DiagnosticOptions *Opts, + DiagnosticConsumer *Client, + bool ShouldOwnClient, + const CodeGenOptions *CodeGenOpts) { + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(new DiagnosticsEngine(DiagID, Opts)); + + // Create the diagnostic client for reporting errors or for + // implementing -verify. + if (Client) { + Diags->setClient(Client, ShouldOwnClient); + } else + Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); + + // Chain in -verify checker, if requested. + if (Opts->VerifyDiagnostics) + Diags->setClient(new VerifyDiagnosticConsumer(*Diags)); + + // Chain in -diagnostic-log-file dumper, if requested. + if (!Opts->DiagnosticLogFile.empty()) + SetUpDiagnosticLog(Opts, CodeGenOpts, *Diags); + + if (!Opts->DiagnosticSerializationFile.empty()) + SetupSerializedDiagnostics(Opts, *Diags, + Opts->DiagnosticSerializationFile); + + // Configure our handling of diagnostics. + ProcessWarningOptions(*Diags, *Opts); + + return Diags; +} + +// File Manager + +void CompilerInstance::createFileManager() { + if (!hasVirtualFileSystem()) { + // TODO: choose the virtual file system based on the CompilerInvocation. + setVirtualFileSystem(vfs::getRealFileSystem()); + } + FileMgr = new FileManager(getFileSystemOpts(), VirtualFileSystem); +} + +// Source Manager + +void CompilerInstance::createSourceManager(FileManager &FileMgr) { + SourceMgr = new SourceManager(getDiagnostics(), FileMgr); +} + +// Initialize the remapping of files to alternative contents, e.g., +// those specified through other files. +static void InitializeFileRemapping(DiagnosticsEngine &Diags, + SourceManager &SourceMgr, + FileManager &FileMgr, + const PreprocessorOptions &InitOpts) { + // Remap files in the source manager (with buffers). + for (const auto &RB : InitOpts.RemappedFileBuffers) { + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = + FileMgr.getVirtualFile(RB.first, RB.second->getBufferSize(), 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) << RB.first; + if (!InitOpts.RetainRemappedFileBuffers) + delete RB.second; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, RB.second, + InitOpts.RetainRemappedFileBuffers); + } + + // Remap files in the source manager (with other files). + for (const auto &RF : InitOpts.RemappedFiles) { + // Find the file that we're mapping to. + const FileEntry *ToFile = FileMgr.getFile(RF.second); + if (!ToFile) { + Diags.Report(diag::err_fe_remap_missing_to_file) << RF.first << RF.second; + continue; + } + + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = + FileMgr.getVirtualFile(RF.first, ToFile->getSize(), 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) << RF.first; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, ToFile); + } + + SourceMgr.setOverridenFilesKeepOriginalName( + InitOpts.RemappedFilesKeepOriginalName); +} + +// Preprocessor + +void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + + // Create a PTH manager if we are using some form of a token cache. + PTHManager *PTHMgr = nullptr; + if (!PPOpts.TokenCache.empty()) + PTHMgr = PTHManager::Create(PPOpts.TokenCache, getDiagnostics()); + + // Create the Preprocessor. + HeaderSearch *HeaderInfo = new HeaderSearch(&getHeaderSearchOpts(), + getSourceManager(), + getDiagnostics(), + getLangOpts(), + &getTarget()); + PP = new Preprocessor(&getPreprocessorOpts(), getDiagnostics(), getLangOpts(), + getSourceManager(), *HeaderInfo, *this, PTHMgr, + /*OwnsHeaderSearch=*/true, TUKind); + PP->Initialize(getTarget()); + + // 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); + PP->setPTHManager(PTHMgr); + } + + if (PPOpts.DetailedRecord) + PP->createPreprocessingRecord(); + + // Apply remappings to the source manager. + InitializeFileRemapping(PP->getDiagnostics(), PP->getSourceManager(), + PP->getFileManager(), PPOpts); + + // Predefine macros and configure the preprocessor. + InitializePreprocessor(*PP, PPOpts, getFrontendOpts()); + + // Initialize the header search object. + ApplyHeaderSearchOptions(PP->getHeaderSearchInfo(), getHeaderSearchOpts(), + PP->getLangOpts(), PP->getTargetInfo().getTriple()); + + PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP); + + // Set up the module path, including the hash for the + // module-creation options. + SmallString<256> SpecificModuleCache( + getHeaderSearchOpts().ModuleCachePath); + if (!getHeaderSearchOpts().DisableModuleHash) + llvm::sys::path::append(SpecificModuleCache, + getInvocation().getModuleHash()); + PP->getHeaderSearchInfo().setModuleCachePath(SpecificModuleCache); + + // Handle generating dependencies, if requested. + const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); + if (!DepOpts.OutputFile.empty()) + TheDependencyFileGenerator.reset( + DependencyFileGenerator::CreateAndAttachToPreprocessor(*PP, DepOpts)); + if (!DepOpts.DOTOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, + getHeaderSearchOpts().Sysroot); + + for (auto &Listener : DependencyCollectors) + Listener->attachToPreprocessor(*PP); + + // If we don't have a collector, but we are collecting module dependencies, + // then we're the top level compiler instance and need to create one. + if (!ModuleDepCollector && !DepOpts.ModuleDependencyOutputDir.empty()) + ModuleDepCollector = std::make_shared<ModuleDependencyCollector>( + DepOpts.ModuleDependencyOutputDir); + + // Handle generating header include information, if requested. + if (DepOpts.ShowHeaderIncludes) + AttachHeaderIncludeGen(*PP); + if (!DepOpts.HeaderIncludeOutputFile.empty()) { + StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; + if (OutputPath == "-") + OutputPath = ""; + AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/true, OutputPath, + /*ShowDepth=*/false); + } + + if (DepOpts.PrintShowIncludes) { + AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/false, /*OutputPath=*/"", + /*ShowDepth=*/true, /*MSStyle=*/true); + } + + // Load all explictly-specified module map files. + for (const auto &Filename : getFrontendOpts().ModuleMapFiles) { + if (auto *File = getFileManager().getFile(Filename)) + PP->getHeaderSearchInfo().loadModuleMapFile(File, /*IsSystem*/false); + else + getDiagnostics().Report(diag::err_module_map_not_found) << Filename; + } +} + +// ASTContext + +void CompilerInstance::createASTContext() { + Preprocessor &PP = getPreprocessor(); + Context = new ASTContext(getLangOpts(), PP.getSourceManager(), + PP.getIdentifierTable(), PP.getSelectorTable(), + PP.getBuiltinInfo()); + Context->InitBuiltinTypes(getTarget()); +} + +// ExternalASTSource + +void CompilerInstance::createPCHExternalASTSource( + StringRef Path, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + void *DeserializationListener, bool OwnDeserializationListener) { + IntrusiveRefCntPtr<ExternalASTSource> Source; + bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + Source = createPCHExternalASTSource( + Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, + AllowPCHWithCompilerErrors, getPreprocessor(), getASTContext(), + DeserializationListener, OwnDeserializationListener, Preamble, + getFrontendOpts().UseGlobalModuleIndex); + ModuleManager = static_cast<ASTReader*>(Source.get()); + getASTContext().setExternalSource(Source); +} + +ExternalASTSource *CompilerInstance::createPCHExternalASTSource( + StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, + bool AllowPCHWithCompilerErrors, Preprocessor &PP, ASTContext &Context, + void *DeserializationListener, bool OwnDeserializationListener, + bool Preamble, bool UseGlobalModuleIndex) { + HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); + + std::unique_ptr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + DisablePCHValidation, + AllowPCHWithCompilerErrors, + /*AllowConfigurationMismatch*/false, + HSOpts.ModulesValidateSystemHeaders, + UseGlobalModuleIndex)); + + Reader->setDeserializationListener( + static_cast<ASTDeserializationListener *>(DeserializationListener), + /*TakeOwnership=*/OwnDeserializationListener); + switch (Reader->ReadAST(Path, + Preamble ? serialization::MK_Preamble + : serialization::MK_PCH, + SourceLocation(), + ASTReader::ARR_None)) { + case ASTReader::Success: + // Set the predefines buffer as suggested by the PCH reader. Typically, the + // predefines buffer will be empty. + PP.setPredefines(Reader->getSuggestedPredefines()); + return Reader.release(); + + case ASTReader::Failure: + // Unrecoverable failure: don't even try to process the input file. + break; + + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + // No suitable PCH file could be found. Return an error. + break; + } + + return nullptr; +} + +// Code Completion + +static bool EnableCodeCompletion(Preprocessor &PP, + const std::string &Filename, + unsigned Line, + unsigned Column) { + // Tell the source manager to chop off the given file at a specific + // line and column. + const FileEntry *Entry = PP.getFileManager().getFile(Filename); + if (!Entry) { + PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file) + << Filename; + return true; + } + + // Truncate the named file at the given line/column. + PP.SetCodeCompletionPoint(Entry, Line, Column); + return false; +} + +void CompilerInstance::createCodeCompletionConsumer() { + const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt; + if (!CompletionConsumer) { + setCodeCompletionConsumer( + createCodeCompletionConsumer(getPreprocessor(), + Loc.FileName, Loc.Line, Loc.Column, + getFrontendOpts().CodeCompleteOpts, + llvm::outs())); + if (!CompletionConsumer) + return; + } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName, + Loc.Line, Loc.Column)) { + setCodeCompletionConsumer(nullptr); + return; + } + + if (CompletionConsumer->isOutputBinary() && + llvm::sys::ChangeStdoutToBinary()) { + getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); + setCodeCompletionConsumer(nullptr); + } +} + +void CompilerInstance::createFrontendTimer() { + FrontendTimer.reset(new llvm::Timer("Clang front-end timer")); +} + +CodeCompleteConsumer * +CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, + const std::string &Filename, + unsigned Line, + unsigned Column, + const CodeCompleteOptions &Opts, + raw_ostream &OS) { + if (EnableCodeCompletion(PP, Filename, Line, Column)) + return nullptr; + + // Set up the creation routine for code-completion. + return new PrintingCodeCompleteConsumer(Opts, OS); +} + +void CompilerInstance::createSema(TranslationUnitKind TUKind, + CodeCompleteConsumer *CompletionConsumer) { + TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), + TUKind, CompletionConsumer)); +} + +// Output Files + +void CompilerInstance::addOutputFile(const OutputFile &OutFile) { + assert(OutFile.OS && "Attempt to add empty stream to output list!"); + OutputFiles.push_back(OutFile); +} + +void CompilerInstance::clearOutputFiles(bool EraseFiles) { + for (std::list<OutputFile>::iterator + it = OutputFiles.begin(), ie = OutputFiles.end(); it != ie; ++it) { + delete it->OS; + if (!it->TempFilename.empty()) { + if (EraseFiles) { + llvm::sys::fs::remove(it->TempFilename); + } else { + SmallString<128> NewOutFile(it->Filename); + + // If '-working-directory' was passed, the output filename should be + // relative to that. + FileMgr->FixupRelativePath(NewOutFile); + if (std::error_code ec = + llvm::sys::fs::rename(it->TempFilename, NewOutFile.str())) { + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << it->TempFilename << it->Filename << ec.message(); + + llvm::sys::fs::remove(it->TempFilename); + } + } + } else if (!it->Filename.empty() && EraseFiles) + llvm::sys::fs::remove(it->Filename); + + } + OutputFiles.clear(); +} + +llvm::raw_fd_ostream * +CompilerInstance::createDefaultOutputFile(bool Binary, + StringRef InFile, + StringRef Extension) { + return createOutputFile(getFrontendOpts().OutputFile, Binary, + /*RemoveFileOnSignal=*/true, InFile, Extension, + /*UseTemporary=*/true); +} + +llvm::raw_null_ostream *CompilerInstance::createNullOutputFile() { + llvm::raw_null_ostream *OS = new llvm::raw_null_ostream(); + addOutputFile(OutputFile("", "", OS)); + return OS; +} + +llvm::raw_fd_ostream * +CompilerInstance::createOutputFile(StringRef OutputPath, + bool Binary, bool RemoveFileOnSignal, + StringRef InFile, + StringRef Extension, + bool UseTemporary, + bool CreateMissingDirectories) { + std::string OutputPathName, TempPathName; + std::error_code EC; + llvm::raw_fd_ostream *OS = createOutputFile( + OutputPath, EC, Binary, RemoveFileOnSignal, InFile, Extension, + UseTemporary, CreateMissingDirectories, &OutputPathName, &TempPathName); + if (!OS) { + getDiagnostics().Report(diag::err_fe_unable_to_open_output) << OutputPath + << EC.message(); + return nullptr; + } + + // Add the output file -- but don't try to remove "-", since this means we are + // using stdin. + addOutputFile(OutputFile((OutputPathName != "-") ? OutputPathName : "", + TempPathName, OS)); + + return OS; +} + +llvm::raw_fd_ostream *CompilerInstance::createOutputFile( + StringRef OutputPath, std::error_code &Error, bool Binary, + bool RemoveFileOnSignal, StringRef InFile, StringRef Extension, + bool UseTemporary, bool CreateMissingDirectories, + std::string *ResultPathName, std::string *TempPathName) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + + std::string OutFile, TempFile; + if (!OutputPath.empty()) { + OutFile = OutputPath; + } else if (InFile == "-") { + OutFile = "-"; + } else if (!Extension.empty()) { + SmallString<128> Path(InFile); + llvm::sys::path::replace_extension(Path, Extension); + OutFile = Path.str(); + } else { + OutFile = "-"; + } + + std::unique_ptr<llvm::raw_fd_ostream> OS; + std::string OSFile; + + if (UseTemporary) { + if (OutFile == "-") + UseTemporary = false; + else { + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(OutputPath, Status); + if (llvm::sys::fs::exists(Status)) { + // Fail early if we can't write to the final destination. + if (!llvm::sys::fs::can_write(OutputPath)) + return nullptr; + + // Don't use a temporary if the output is a special file. This handles + // things like '-o /dev/null' + if (!llvm::sys::fs::is_regular_file(Status)) + UseTemporary = false; + } + } + } + + if (UseTemporary) { + // Create a temporary file. + SmallString<128> TempPath; + TempPath = OutFile; + TempPath += "-%%%%%%%%"; + int fd; + std::error_code EC = + llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath); + + if (CreateMissingDirectories && + EC == llvm::errc::no_such_file_or_directory) { + StringRef Parent = llvm::sys::path::parent_path(OutputPath); + EC = llvm::sys::fs::create_directories(Parent); + if (!EC) { + EC = llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath); + } + } + + if (!EC) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + OSFile = TempFile = TempPath.str(); + } + // If we failed to create the temporary, fallback to writing to the file + // directly. This handles the corner case where we cannot write to the + // directory, but can write to the file. + } + + if (!OS) { + OSFile = OutFile; + OS.reset(new llvm::raw_fd_ostream( + OSFile, Error, + (Binary ? llvm::sys::fs::F_None : llvm::sys::fs::F_Text))); + if (Error) + return nullptr; + } + + // Make sure the out stream file gets removed if we crash. + if (RemoveFileOnSignal) + llvm::sys::RemoveFileOnSignal(OSFile); + + if (ResultPathName) + *ResultPathName = OutFile; + if (TempPathName) + *TempPathName = TempFile; + + return OS.release(); +} + +// Initialization Utilities + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ + return InitializeSourceManager(Input, getDiagnostics(), + getFileManager(), getSourceManager(), + getFrontendOpts()); +} + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, + DiagnosticsEngine &Diags, + FileManager &FileMgr, + SourceManager &SourceMgr, + const FrontendOptions &Opts) { + SrcMgr::CharacteristicKind + Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; + + if (Input.isBuffer()) { + SourceMgr.setMainFileID(SourceMgr.createFileID( + std::unique_ptr<llvm::MemoryBuffer>(Input.getBuffer()), Kind)); + assert(!SourceMgr.getMainFileID().isInvalid() && + "Couldn't establish MainFileID!"); + return true; + } + + StringRef InputFile = Input.getFile(); + + // Figure out where to get and map in the main file. + if (InputFile != "-") { + const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + if (!File) { + Diags.Report(diag::err_fe_error_reading) << InputFile; + return false; + } + + // The natural SourceManager infrastructure can't currently handle named + // pipes, but we would at least like to accept them for the main + // file. Detect them here, read them with the volatile flag so FileMgr will + // pick up the correct size, and simply override their contents as we do for + // STDIN. + if (File->isNamedPipe()) { + auto MB = FileMgr.getBufferForFile(File, /*isVolatile=*/true); + if (MB) { + // Create a new virtual file that will have the correct size. + File = FileMgr.getVirtualFile(InputFile, (*MB)->getBufferSize(), 0); + SourceMgr.overrideFileContents(File, std::move(*MB)); + } else { + Diags.Report(diag::err_cannot_open_file) << InputFile + << MB.getError().message(); + return false; + } + } + + SourceMgr.setMainFileID( + SourceMgr.createFileID(File, SourceLocation(), Kind)); + } else { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> SBOrErr = + llvm::MemoryBuffer::getSTDIN(); + if (std::error_code EC = SBOrErr.getError()) { + Diags.Report(diag::err_fe_error_reading_stdin) << EC.message(); + return false; + } + std::unique_ptr<llvm::MemoryBuffer> SB = std::move(SBOrErr.get()); + + const FileEntry *File = FileMgr.getVirtualFile(SB->getBufferIdentifier(), + SB->getBufferSize(), 0); + SourceMgr.setMainFileID( + SourceMgr.createFileID(File, SourceLocation(), Kind)); + SourceMgr.overrideFileContents(File, std::move(SB)); + } + + assert(!SourceMgr.getMainFileID().isInvalid() && + "Couldn't establish MainFileID!"); + return true; +} + +// High-Level Operations + +bool CompilerInstance::ExecuteAction(FrontendAction &Act) { + assert(hasDiagnostics() && "Diagnostics engine is not initialized!"); + assert(!getFrontendOpts().ShowHelp && "Client must handle '-help'!"); + assert(!getFrontendOpts().ShowVersion && "Client must handle '-version'!"); + + // FIXME: Take this as an argument, once all the APIs we used have moved to + // taking it as an input instead of hard-coding llvm::errs. + raw_ostream &OS = llvm::errs(); + + // Create the target instance. + setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), + getInvocation().TargetOpts)); + if (!hasTarget()) + return false; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + getTarget().adjust(getLangOpts()); + + // rewriter project will change target built-in bool type from its default. + if (getFrontendOpts().ProgramAction == frontend::RewriteObjC) + getTarget().noSignedCharForObjCBool(); + + // Validate/process some options. + if (getHeaderSearchOpts().Verbose) + OS << "clang -cc1 version " CLANG_VERSION_STRING + << " based upon " << BACKEND_PACKAGE_STRING + << " default target " << llvm::sys::getDefaultTargetTriple() << "\n"; + + if (getFrontendOpts().ShowTimers) + createFrontendTimer(); + + if (getFrontendOpts().ShowStats) + llvm::EnableStatistics(); + + for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { + // Reset the ID tables if we are reusing the SourceManager and parsing + // regular files. + if (hasSourceManager() && !Act.isModelParsingAction()) + getSourceManager().clearIDTables(); + + if (Act.BeginSourceFile(*this, getFrontendOpts().Inputs[i])) { + Act.Execute(); + Act.EndSourceFile(); + } + } + + // Notify the diagnostic client that all files were processed. + getDiagnostics().getClient()->finish(); + + if (getDiagnosticOpts().ShowCarets) { + // We can have multiple diagnostics sharing one diagnostic client. + // Get the total number of warnings/errors from the client. + unsigned NumWarnings = getDiagnostics().getClient()->getNumWarnings(); + unsigned NumErrors = getDiagnostics().getClient()->getNumErrors(); + + if (NumWarnings) + OS << NumWarnings << " warning" << (NumWarnings == 1 ? "" : "s"); + if (NumWarnings && NumErrors) + OS << " and "; + if (NumErrors) + OS << NumErrors << " error" << (NumErrors == 1 ? "" : "s"); + if (NumWarnings || NumErrors) + OS << " generated.\n"; + } + + if (getFrontendOpts().ShowStats && hasFileManager()) { + getFileManager().PrintStats(); + OS << "\n"; + } + + return !getDiagnostics().getClient()->getNumErrors(); +} + +/// \brief Determine the appropriate source input kind based on language +/// options. +static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) { + if (LangOpts.OpenCL) + return IK_OpenCL; + if (LangOpts.CUDA) + return IK_CUDA; + if (LangOpts.ObjC1) + return LangOpts.CPlusPlus? IK_ObjCXX : IK_ObjC; + return LangOpts.CPlusPlus? IK_CXX : IK_C; +} + +/// \brief Compile a module file for the given module, using the options +/// provided by the importing compiler instance. Returns true if the module +/// was built without errors. +static bool compileModuleImpl(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + Module *Module, + StringRef ModuleFileName) { + ModuleMap &ModMap + = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + + // Construct a compiler invocation for creating this module. + IntrusiveRefCntPtr<CompilerInvocation> Invocation + (new CompilerInvocation(ImportingInstance.getInvocation())); + + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + + // For any options that aren't intended to affect how a module is built, + // reset them to their default values. + Invocation->getLangOpts()->resetNonModularOptions(); + PPOpts.resetNonModularOptions(); + + // Remove any macro definitions that are explicitly ignored by the module. + // They aren't supposed to affect how the module is built anyway. + const HeaderSearchOptions &HSOpts = Invocation->getHeaderSearchOpts(); + PPOpts.Macros.erase( + std::remove_if(PPOpts.Macros.begin(), PPOpts.Macros.end(), + [&HSOpts](const std::pair<std::string, bool> &def) { + StringRef MacroDef = def.first; + return HSOpts.ModulesIgnoreMacros.count(MacroDef.split('=').first) > 0; + }), + PPOpts.Macros.end()); + + // Note the name of the module we're building. + Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName(); + + // Make sure that the failed-module structure has been allocated in + // the importing instance, and propagate the pointer to the newly-created + // instance. + PreprocessorOptions &ImportingPPOpts + = ImportingInstance.getInvocation().getPreprocessorOpts(); + if (!ImportingPPOpts.FailedModules) + ImportingPPOpts.FailedModules = new PreprocessorOptions::FailedModulesSet; + PPOpts.FailedModules = ImportingPPOpts.FailedModules; + + // If there is a module map file, build the module using the module map. + // Set up the inputs/outputs so that we build the module from its umbrella + // header. + FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); + FrontendOpts.OutputFile = ModuleFileName.str(); + FrontendOpts.DisableFree = false; + FrontendOpts.GenerateGlobalModuleIndex = false; + FrontendOpts.Inputs.clear(); + InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); + + // Don't free the remapped file buffers; they are owned by our caller. + PPOpts.RetainRemappedFileBuffers = true; + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + assert(ImportingInstance.getInvocation().getModuleHash() == + Invocation->getModuleHash() && "Module hash mismatch!"); + + // Construct a compiler instance that will be used to actually create the + // module. + CompilerInstance Instance(/*BuildingModule=*/true); + Instance.setInvocation(&*Invocation); + + Instance.createDiagnostics(new ForwardingDiagnosticConsumer( + ImportingInstance.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + Instance.setVirtualFileSystem(&ImportingInstance.getVirtualFileSystem()); + + // Note that this module is part of the module build stack, so that we + // can detect cycles in the module graph. + Instance.setFileManager(&ImportingInstance.getFileManager()); + Instance.createSourceManager(Instance.getFileManager()); + SourceManager &SourceMgr = Instance.getSourceManager(); + SourceMgr.setModuleBuildStack( + ImportingInstance.getSourceManager().getModuleBuildStack()); + SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(), + FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + + // If we're collecting module dependencies, we need to share a collector + // between all of the module CompilerInstances. + Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector()); + + // Get or create the module map that we'll use to build this module. + std::string InferredModuleMapContent; + if (const FileEntry *ModuleMapFile = + ModMap.getContainingModuleMapFile(Module)) { + // Use the module map where this module resides. + FrontendOpts.Inputs.push_back( + FrontendInputFile(ModuleMapFile->getName(), IK)); + } else { + llvm::raw_string_ostream OS(InferredModuleMapContent); + Module->print(OS); + OS.flush(); + FrontendOpts.Inputs.push_back( + FrontendInputFile("__inferred_module.map", IK)); + + std::unique_ptr<llvm::MemoryBuffer> ModuleMapBuffer = + llvm::MemoryBuffer::getMemBuffer(InferredModuleMapContent); + ModuleMapFile = Instance.getFileManager().getVirtualFile( + "__inferred_module.map", InferredModuleMapContent.size(), 0); + SourceMgr.overrideFileContents(ModuleMapFile, std::move(ModuleMapBuffer)); + } + + // Construct a module-generating action. Passing through the module map is + // safe because the FileManager is shared between the compiler instances. + GenerateModuleAction CreateModuleAction( + ModMap.getModuleMapFileForUniquing(Module), Module->IsSystem); + + ImportingInstance.getDiagnostics().Report(ImportLoc, + diag::remark_module_build) + << Module->Name << ModuleFileName; + + // Execute the action to actually build the module in-place. Use a separate + // thread so that we get a stack large enough. + const unsigned ThreadStackSize = 8 << 20; + llvm::CrashRecoveryContext CRC; + CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(CreateModuleAction); }, + ThreadStackSize); + + ImportingInstance.getDiagnostics().Report(ImportLoc, + diag::remark_module_build_done) + << Module->Name; + + // Delete the temporary module map file. + // FIXME: Even though we're executing under crash protection, it would still + // be nice to do this with RemoveFileOnSignal when we can. However, that + // doesn't make sense for all clients, so clean this up manually. + Instance.clearOutputFiles(/*EraseFiles=*/true); + + // We've rebuilt a module. If we're allowed to generate or update the global + // module index, record that fact in the importing compiler instance. + if (ImportingInstance.getFrontendOpts().GenerateGlobalModuleIndex) { + ImportingInstance.setBuildGlobalModuleIndex(true); + } + + return !Instance.getDiagnostics().hasErrorOccurred(); +} + +static bool compileAndLoadModule(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, Module *Module, + StringRef ModuleFileName) { + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); + + auto diagnoseBuildFailure = [&] { + Diags.Report(ModuleNameLoc, diag::err_module_not_built) + << Module->Name << SourceRange(ImportLoc, ModuleNameLoc); + }; + + // FIXME: have LockFileManager return an error_code so that we can + // avoid the mkdir when the directory already exists. + StringRef Dir = llvm::sys::path::parent_path(ModuleFileName); + llvm::sys::fs::create_directories(Dir); + + while (1) { + unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; + llvm::LockFileManager Locked(ModuleFileName); + switch (Locked) { + case llvm::LockFileManager::LFS_Error: + Diags.Report(ModuleNameLoc, diag::err_module_lock_failure) + << Module->Name; + return false; + + case llvm::LockFileManager::LFS_Owned: + // We're responsible for building the module ourselves. + if (!compileModuleImpl(ImportingInstance, ModuleNameLoc, Module, + ModuleFileName)) { + diagnoseBuildFailure(); + return false; + } + break; + + case llvm::LockFileManager::LFS_Shared: + // Someone else is responsible for building the module. Wait for them to + // finish. + if (Locked.waitForUnlock() == llvm::LockFileManager::Res_OwnerDied) + continue; // try again to get the lock. + ModuleLoadCapabilities |= ASTReader::ARR_OutOfDate; + break; + } + + // Try to read the module file, now that we've compiled it. + ASTReader::ASTReadResult ReadResult = + ImportingInstance.getModuleManager()->ReadAST( + ModuleFileName, serialization::MK_ImplicitModule, ImportLoc, + ModuleLoadCapabilities); + + if (ReadResult == ASTReader::OutOfDate && + Locked == llvm::LockFileManager::LFS_Shared) { + // The module may be out of date in the presence of file system races, + // or if one of its imports depends on header search paths that are not + // consistent with this ImportingInstance. Try again... + continue; + } else if (ReadResult == ASTReader::Missing) { + diagnoseBuildFailure(); + } else if (ReadResult != ASTReader::Success && + !Diags.hasErrorOccurred()) { + // The ASTReader didn't diagnose the error, so conservatively report it. + diagnoseBuildFailure(); + } + return ReadResult == ASTReader::Success; + } +} + +/// \brief Diagnose differences between the current definition of the given +/// configuration macro and the definition provided on the command line. +static void checkConfigMacro(Preprocessor &PP, StringRef ConfigMacro, + Module *Mod, SourceLocation ImportLoc) { + IdentifierInfo *Id = PP.getIdentifierInfo(ConfigMacro); + SourceManager &SourceMgr = PP.getSourceManager(); + + // If this identifier has never had a macro definition, then it could + // not have changed. + if (!Id->hadMacroDefinition()) + return; + + // If this identifier does not currently have a macro definition, + // check whether it had one on the command line. + if (!Id->hasMacroDefinition()) { + MacroDirective::DefInfo LatestDef = + PP.getMacroDirectiveHistory(Id)->getDefinition(); + for (MacroDirective::DefInfo Def = LatestDef; Def; + Def = Def.getPreviousDefinition()) { + FileID FID = SourceMgr.getFileID(Def.getLocation()); + if (FID.isInvalid()) + continue; + + // We only care about the predefines buffer. + if (FID != PP.getPredefinesFileID()) + continue; + + // This macro was defined on the command line, then #undef'd later. + // Complain. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << true << ConfigMacro << Mod->getFullModuleName(); + if (LatestDef.isUndefined()) + PP.Diag(LatestDef.getUndefLocation(), diag::note_module_def_undef_here) + << true; + return; + } + + // Okay: no definition in the predefines buffer. + return; + } + + // This identifier has a macro definition. Check whether we had a definition + // on the command line. + MacroDirective::DefInfo LatestDef = + PP.getMacroDirectiveHistory(Id)->getDefinition(); + MacroDirective::DefInfo PredefinedDef; + for (MacroDirective::DefInfo Def = LatestDef; Def; + Def = Def.getPreviousDefinition()) { + FileID FID = SourceMgr.getFileID(Def.getLocation()); + if (FID.isInvalid()) + continue; + + // We only care about the predefines buffer. + if (FID != PP.getPredefinesFileID()) + continue; + + PredefinedDef = Def; + break; + } + + // If there was no definition for this macro in the predefines buffer, + // complain. + if (!PredefinedDef || + (!PredefinedDef.getLocation().isValid() && + PredefinedDef.getUndefLocation().isValid())) { + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(LatestDef.getLocation(), diag::note_module_def_undef_here) + << false; + return; + } + + // If the current macro definition is the same as the predefined macro + // definition, it's okay. + if (LatestDef.getMacroInfo() == PredefinedDef.getMacroInfo() || + LatestDef.getMacroInfo()->isIdenticalTo(*PredefinedDef.getMacroInfo(),PP, + /*Syntactically=*/true)) + return; + + // The macro definitions differ. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(LatestDef.getLocation(), diag::note_module_def_undef_here) + << false; +} + +/// \brief Write a new timestamp file with the given path. +static void writeTimestampFile(StringRef TimestampFile) { + std::error_code EC; + llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::F_None); +} + +/// \brief Prune the module cache of modules that haven't been accessed in +/// a long time. +static void pruneModuleCache(const HeaderSearchOptions &HSOpts) { + struct stat StatBuf; + llvm::SmallString<128> TimestampFile; + TimestampFile = HSOpts.ModuleCachePath; + llvm::sys::path::append(TimestampFile, "modules.timestamp"); + + // Try to stat() the timestamp file. + if (::stat(TimestampFile.c_str(), &StatBuf)) { + // If the timestamp file wasn't there, create one now. + if (errno == ENOENT) { + writeTimestampFile(TimestampFile); + } + return; + } + + // Check whether the time stamp is older than our pruning interval. + // If not, do nothing. + time_t TimeStampModTime = StatBuf.st_mtime; + time_t CurrentTime = time(nullptr); + if (CurrentTime - TimeStampModTime <= time_t(HSOpts.ModuleCachePruneInterval)) + return; + + // Write a new timestamp file so that nobody else attempts to prune. + // There is a benign race condition here, if two Clang instances happen to + // notice at the same time that the timestamp is out-of-date. + writeTimestampFile(TimestampFile); + + // Walk the entire module cache, looking for unused module files and module + // indices. + std::error_code EC; + SmallString<128> ModuleCachePathNative; + llvm::sys::path::native(HSOpts.ModuleCachePath, ModuleCachePathNative); + for (llvm::sys::fs::directory_iterator + Dir(ModuleCachePathNative.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // If we don't have a directory, there's nothing to look into. + if (!llvm::sys::fs::is_directory(Dir->path())) + continue; + + // Walk all of the files within this directory. + for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd; + File != FileEnd && !EC; File.increment(EC)) { + // We only care about module and global module index files. + StringRef Extension = llvm::sys::path::extension(File->path()); + if (Extension != ".pcm" && Extension != ".timestamp" && + llvm::sys::path::filename(File->path()) != "modules.idx") + continue; + + // Look at this file. If we can't stat it, there's nothing interesting + // there. + if (::stat(File->path().c_str(), &StatBuf)) + continue; + + // If the file has been used recently enough, leave it there. + time_t FileAccessTime = StatBuf.st_atime; + if (CurrentTime - FileAccessTime <= + time_t(HSOpts.ModuleCachePruneAfter)) { + continue; + } + + // Remove the file. + llvm::sys::fs::remove(File->path()); + + // Remove the timestamp file. + std::string TimpestampFilename = File->path() + ".timestamp"; + llvm::sys::fs::remove(TimpestampFilename); + } + + // If we removed all of the files in the directory, remove the directory + // itself. + if (llvm::sys::fs::directory_iterator(Dir->path(), EC) == + llvm::sys::fs::directory_iterator() && !EC) + llvm::sys::fs::remove(Dir->path()); + } +} + +void CompilerInstance::createModuleManager() { + if (!ModuleManager) { + if (!hasASTContext()) + createASTContext(); + + // If we're not recursively building a module, check whether we + // need to prune the module cache. + if (getSourceManager().getModuleBuildStack().empty() && + getHeaderSearchOpts().ModuleCachePruneInterval > 0 && + getHeaderSearchOpts().ModuleCachePruneAfter > 0) { + pruneModuleCache(getHeaderSearchOpts()); + } + + HeaderSearchOptions &HSOpts = getHeaderSearchOpts(); + std::string Sysroot = HSOpts.Sysroot; + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + ModuleManager = new ASTReader(getPreprocessor(), *Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHValidation, + /*AllowASTWithCompilerErrors=*/false, + /*AllowConfigurationMismatch=*/false, + HSOpts.ModulesValidateSystemHeaders, + getFrontendOpts().UseGlobalModuleIndex); + if (hasASTConsumer()) { + ModuleManager->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + getASTContext().setExternalSource(ModuleManager); + if (hasSema()) + ModuleManager->InitializeSema(getSema()); + if (hasASTConsumer()) + ModuleManager->StartTranslationUnit(&getASTConsumer()); + } +} + +bool CompilerInstance::loadModuleFile(StringRef FileName) { + // Helper to recursively read the module names for all modules we're adding. + // We mark these as known and redirect any attempt to load that module to + // the files we were handed. + struct ReadModuleNames : ASTReaderListener { + CompilerInstance &CI; + std::vector<StringRef> ModuleFileStack; + bool Failed; + bool TopFileIsModule; + + ReadModuleNames(CompilerInstance &CI) + : CI(CI), Failed(false), TopFileIsModule(false) {} + + bool needsImportVisitation() const override { return true; } + + void visitImport(StringRef FileName) override { + ModuleFileStack.push_back(FileName); + if (ASTReader::readASTFileControlBlock(FileName, CI.getFileManager(), + *this)) { + CI.getDiagnostics().Report(SourceLocation(), + diag::err_module_file_not_found) + << FileName; + // FIXME: Produce a note stack explaining how we got here. + Failed = true; + } + ModuleFileStack.pop_back(); + } + + void ReadModuleName(StringRef ModuleName) override { + if (ModuleFileStack.size() == 1) + TopFileIsModule = true; + + auto &ModuleFile = CI.ModuleFileOverrides[ModuleName]; + if (!ModuleFile.empty() && + CI.getFileManager().getFile(ModuleFile) != + CI.getFileManager().getFile(ModuleFileStack.back())) + CI.getDiagnostics().Report(SourceLocation(), + diag::err_conflicting_module_files) + << ModuleName << ModuleFile << ModuleFileStack.back(); + ModuleFile = ModuleFileStack.back(); + } + } RMN(*this); + + RMN.visitImport(FileName); + + if (RMN.Failed) + return false; + + // If we never found a module name for the top file, then it's not a module, + // it's a PCH or preamble or something. + if (!RMN.TopFileIsModule) { + getDiagnostics().Report(SourceLocation(), diag::err_module_file_not_module) + << FileName; + return false; + } + + return true; +} + +ModuleLoadResult +CompilerInstance::loadModule(SourceLocation ImportLoc, + ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + // Determine what file we're searching from. + StringRef ModuleName = Path[0].first->getName(); + SourceLocation ModuleNameLoc = Path[0].second; + + // If we've already handled this import, just return the cached result. + // This one-element cache is important to eliminate redundant diagnostics + // when both the preprocessor and parser see the same import declaration. + if (!ImportLoc.isInvalid() && LastModuleImportLoc == ImportLoc) { + // Make the named module visible. + if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule && + ModuleName != getLangOpts().ImplementationOfModule) + ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility, + ImportLoc, /*Complain=*/false); + return LastModuleImportResult; + } + + clang::Module *Module = nullptr; + + // If we don't already have information on this module, load the module now. + llvm::DenseMap<const IdentifierInfo *, clang::Module *>::iterator Known + = KnownModules.find(Path[0].first); + if (Known != KnownModules.end()) { + // Retrieve the cached top-level module. + Module = Known->second; + } else if (ModuleName == getLangOpts().CurrentModule || + ModuleName == getLangOpts().ImplementationOfModule) { + // This is the module we're building. + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } else { + // Search for a module with the given name. + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + if (!Module) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + auto Override = ModuleFileOverrides.find(ModuleName); + bool Explicit = Override != ModuleFileOverrides.end(); + + std::string ModuleFileName = + Explicit ? Override->second + : PP->getHeaderSearchInfo().getModuleFileName(Module); + + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) + createModuleManager(); + + if (TheDependencyFileGenerator) + TheDependencyFileGenerator->AttachToASTReader(*ModuleManager); + + if (ModuleDepCollector) + ModuleDepCollector->attachToASTReader(*ModuleManager); + + for (auto &Listener : DependencyCollectors) + Listener->attachToASTReader(*ModuleManager); + + // Try to load the module file. + unsigned ARRFlags = + Explicit ? 0 : ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing; + switch (ModuleManager->ReadAST(ModuleFileName, + Explicit ? serialization::MK_ExplicitModule + : serialization::MK_ImplicitModule, + ImportLoc, ARRFlags)) { + case ASTReader::Success: + break; + + case ASTReader::OutOfDate: + case ASTReader::Missing: { + if (Explicit) { + // ReadAST has already complained for us. + ModuleLoader::HadFatalFailure = true; + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + } + + // The module file is missing or out-of-date. Build it. + assert(Module && "missing module file"); + // Check whether there is a cycle in the module graph. + ModuleBuildStack ModPath = getSourceManager().getModuleBuildStack(); + ModuleBuildStack::iterator Pos = ModPath.begin(), PosEnd = ModPath.end(); + for (; Pos != PosEnd; ++Pos) { + if (Pos->first == ModuleName) + break; + } + + if (Pos != PosEnd) { + SmallString<256> CyclePath; + for (; Pos != PosEnd; ++Pos) { + CyclePath += Pos->first; + CyclePath += " -> "; + } + CyclePath += ModuleName; + + getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) + << ModuleName << CyclePath; + return ModuleLoadResult(); + } + + // Check whether we have already attempted to build this module (but + // failed). + if (getPreprocessorOpts().FailedModules && + getPreprocessorOpts().FailedModules->hasAlreadyFailed(ModuleName)) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_built) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Try to compile and then load the module. + if (!compileAndLoadModule(*this, ImportLoc, ModuleNameLoc, Module, + ModuleFileName)) { + assert(getDiagnostics().hasErrorOccurred() && + "undiagnosed error in compileAndLoadModule"); + if (getPreprocessorOpts().FailedModules) + getPreprocessorOpts().FailedModules->addFailed(ModuleName); + KnownModules[Path[0].first] = nullptr; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Okay, we've rebuilt and now loaded the module. + break; + } + + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + ModuleLoader::HadFatalFailure = true; + // FIXME: The ASTReader will already have complained, but can we showhorn + // that diagnostic information into a more useful form? + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + + case ASTReader::Failure: + ModuleLoader::HadFatalFailure = true; + // Already complained, but note now that we failed. + KnownModules[Path[0].first] = nullptr; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Cache the result of this top-level module lookup for later. + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } + + // If we never found the module, fail. + if (!Module) + return ModuleLoadResult(); + + // Verify that the rest of the module path actually corresponds to + // a submodule. + if (Path.size() > 1) { + for (unsigned I = 1, N = Path.size(); I != N; ++I) { + StringRef Name = Path[I].first->getName(); + clang::Module *Sub = Module->findSubmodule(Name); + + if (!Sub) { + // Attempt to perform typo correction to find a module name that works. + SmallVector<StringRef, 2> Best; + unsigned BestEditDistance = (std::numeric_limits<unsigned>::max)(); + + for (clang::Module::submodule_iterator J = Module->submodule_begin(), + JEnd = Module->submodule_end(); + J != JEnd; ++J) { + unsigned ED = Name.edit_distance((*J)->Name, + /*AllowReplacements=*/true, + BestEditDistance); + if (ED <= BestEditDistance) { + if (ED < BestEditDistance) { + Best.clear(); + BestEditDistance = ED; + } + + Best.push_back((*J)->Name); + } + } + + // If there was a clear winner, user it. + if (Best.size() == 1) { + getDiagnostics().Report(Path[I].second, + diag::err_no_submodule_suggest) + << Path[I].first << Module->getFullModuleName() << Best[0] + << SourceRange(Path[0].second, Path[I-1].second) + << FixItHint::CreateReplacement(SourceRange(Path[I].second), + Best[0]); + + Sub = Module->findSubmodule(Best[0]); + } + } + + if (!Sub) { + // No submodule by this name. Complain, and don't look for further + // submodules. + getDiagnostics().Report(Path[I].second, diag::err_no_submodule) + << Path[I].first << Module->getFullModuleName() + << SourceRange(Path[0].second, Path[I-1].second); + break; + } + + Module = Sub; + } + } + + // Don't make the module visible if we are in the implementation. + if (ModuleName == getLangOpts().ImplementationOfModule) + return ModuleLoadResult(Module, false); + + // Make the named module visible, if it's not already part of the module + // we are parsing. + if (ModuleName != getLangOpts().CurrentModule) { + if (!Module->IsFromModuleFile) { + // We have an umbrella header or directory that doesn't actually include + // all of the headers within the directory it covers. Complain about + // this missing submodule and recover by forgetting that we ever saw + // this submodule. + // FIXME: Should we detect this at module load time? It seems fairly + // expensive (and rare). + getDiagnostics().Report(ImportLoc, diag::warn_missing_submodule) + << Module->getFullModuleName() + << SourceRange(Path.front().second, Path.back().second); + + return ModuleLoadResult(nullptr, true); + } + + // Check whether this module is available. + clang::Module::Requirement Requirement; + clang::Module::UnresolvedHeaderDirective MissingHeader; + if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement, + MissingHeader)) { + if (MissingHeader.FileNameLoc.isValid()) { + getDiagnostics().Report(MissingHeader.FileNameLoc, + diag::err_module_header_missing) + << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else { + getDiagnostics().Report(ImportLoc, diag::err_module_unavailable) + << Module->getFullModuleName() + << Requirement.second << Requirement.first + << SourceRange(Path.front().second, Path.back().second); + } + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(); + return ModuleLoadResult(); + } + + ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc, + /*Complain=*/true); + } + + // Check for any configuration macros that have changed. + clang::Module *TopModule = Module->getTopLevelModule(); + for (unsigned I = 0, N = TopModule->ConfigMacros.size(); I != N; ++I) { + checkConfigMacro(getPreprocessor(), TopModule->ConfigMacros[I], + Module, ImportLoc); + } + + // Determine whether we're in the #include buffer for a module. The #includes + // in that buffer do not qualify as module imports; they're just an + // implementation detail of us building the module. + bool IsInModuleIncludes = !getLangOpts().CurrentModule.empty() && + getSourceManager().getFileID(ImportLoc) == + getSourceManager().getMainFileID(); + + // If this module import was due to an inclusion directive, create an + // implicit import declaration to capture it in the AST. + if (IsInclusionDirective && hasASTContext() && !IsInModuleIncludes) { + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, + ImportLoc, Module, + Path.back().second); + TU->addDecl(ImportD); + if (Consumer) + Consumer->HandleImplicitImportDecl(ImportD); + } + + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(Module, false); + return LastModuleImportResult; +} + +void CompilerInstance::makeModuleVisible(Module *Mod, + Module::NameVisibilityKind Visibility, + SourceLocation ImportLoc, + bool Complain){ + ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc, Complain); +} + +GlobalModuleIndex *CompilerInstance::loadGlobalModuleIndex( + SourceLocation TriggerLoc) { + if (!ModuleManager) + createModuleManager(); + // Can't do anything if we don't have the module manager. + if (!ModuleManager) + return nullptr; + // Get an existing global index. This loads it if not already + // loaded. + ModuleManager->loadGlobalIndex(); + GlobalModuleIndex *GlobalIndex = ModuleManager->getGlobalIndex(); + // If the global index doesn't exist, create it. + if (!GlobalIndex && shouldBuildGlobalModuleIndex() && hasFileManager() && + hasPreprocessor()) { + llvm::sys::fs::create_directories( + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + GlobalModuleIndex::writeIndex( + getFileManager(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + ModuleManager->resetForReload(); + ModuleManager->loadGlobalIndex(); + GlobalIndex = ModuleManager->getGlobalIndex(); + } + // For finding modules needing to be imported for fixit messages, + // we need to make the global index cover all modules, so we do that here. + if (!HaveFullGlobalModuleIndex && GlobalIndex && !buildingModule()) { + ModuleMap &MMap = getPreprocessor().getHeaderSearchInfo().getModuleMap(); + bool RecreateIndex = false; + for (ModuleMap::module_iterator I = MMap.module_begin(), + E = MMap.module_end(); I != E; ++I) { + Module *TheModule = I->second; + const FileEntry *Entry = TheModule->getASTFile(); + if (!Entry) { + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; + Path.push_back(std::make_pair( + getPreprocessor().getIdentifierInfo(TheModule->Name), TriggerLoc)); + std::reverse(Path.begin(), Path.end()); + // Load a module as hidden. This also adds it to the global index. + loadModule(TheModule->DefinitionLoc, Path, + Module::Hidden, false); + RecreateIndex = true; + } + } + if (RecreateIndex) { + GlobalModuleIndex::writeIndex( + getFileManager(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + ModuleManager->resetForReload(); + ModuleManager->loadGlobalIndex(); + GlobalIndex = ModuleManager->getGlobalIndex(); + } + HaveFullGlobalModuleIndex = true; + } + return GlobalIndex; +} + +// Check global module index for missing imports. +bool +CompilerInstance::lookupMissingImports(StringRef Name, + SourceLocation TriggerLoc) { + // Look for the symbol in non-imported modules, but only if an error + // actually occurred. + if (!buildingModule()) { + // Load global module index, or retrieve a previously loaded one. + GlobalModuleIndex *GlobalIndex = loadGlobalModuleIndex( + TriggerLoc); + + // Only if we have a global index. + if (GlobalIndex) { + GlobalModuleIndex::HitSet FoundModules; + + // Find the modules that reference the identifier. + // Note that this only finds top-level modules. + // We'll let diagnoseTypo find the actual declaration module. + if (GlobalIndex->lookupIdentifier(Name, FoundModules)) + return true; + } + } + + return false; +} +void CompilerInstance::resetAndLeakSema() { BuryPointer(takeSema()); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp new file mode 100644 index 0000000..54025b0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -0,0 +1,2090 @@ +//===--- CompilerInvocation.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Util.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/LangStandard.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include <atomic> +#include <memory> +#include <sys/stat.h> +#include <system_error> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Initialization. +//===----------------------------------------------------------------------===// + +CompilerInvocationBase::CompilerInvocationBase() + : LangOpts(new LangOptions()), TargetOpts(new TargetOptions()), + DiagnosticOpts(new DiagnosticOptions()), + HeaderSearchOpts(new HeaderSearchOptions()), + PreprocessorOpts(new PreprocessorOptions()) {} + +CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X) + : RefCountedBase<CompilerInvocation>(), + LangOpts(new LangOptions(*X.getLangOpts())), + TargetOpts(new TargetOptions(X.getTargetOpts())), + DiagnosticOpts(new DiagnosticOptions(X.getDiagnosticOpts())), + HeaderSearchOpts(new HeaderSearchOptions(X.getHeaderSearchOpts())), + PreprocessorOpts(new PreprocessorOptions(X.getPreprocessorOpts())) {} + +CompilerInvocationBase::~CompilerInvocationBase() {} + +//===----------------------------------------------------------------------===// +// Deserialization (from args) +//===----------------------------------------------------------------------===// + +using namespace clang::driver; +using namespace clang::driver::options; +using namespace llvm::opt; + +// + +static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + unsigned DefaultOpt = 0; + if (IK == IK_OpenCL && !Args.hasArg(OPT_cl_opt_disable)) + DefaultOpt = 2; + + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O0)) + return 0; + + if (A->getOption().matches(options::OPT_Ofast)) + return 3; + + assert (A->getOption().matches(options::OPT_O)); + + StringRef S(A->getValue()); + if (S == "s" || S == "z" || S.empty()) + return 2; + + return getLastArgIntValue(Args, OPT_O, DefaultOpt, Diags); + } + + return DefaultOpt; +} + +static unsigned getOptimizationLevelSize(ArgList &Args) { + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O)) { + switch (A->getValue()[0]) { + default: + return 0; + case 's': + return 1; + case 'z': + return 2; + } + } + } + return 0; +} + +static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group, + OptSpecifier GroupWithValue, + std::vector<std::string> &Diagnostics) { + for (Arg *A : Args.filtered(Group)) { + if (A->getOption().getKind() == Option::FlagClass) { + // The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add + // its name (minus the "W" or "R" at the beginning) to the warning list. + Diagnostics.push_back(A->getOption().getName().drop_front(1)); + } else if (A->getOption().matches(GroupWithValue)) { + // This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic group. + Diagnostics.push_back(A->getOption().getName().drop_front(1).rtrim("=-")); + } else { + // Otherwise, add its value (for OPT_W_Joined and similar). + for (const char *Arg : A->getValues()) + Diagnostics.push_back(Arg); + } + } +} + +static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + using namespace options; + bool Success = true; + if (Arg *A = Args.getLastArg(OPT_analyzer_store)) { + StringRef Name = A->getValue(); + AnalysisStores Value = llvm::StringSwitch<AnalysisStores>(Name) +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumStores); + if (Value == NumStores) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisStoreOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_constraints)) { + StringRef Name = A->getValue(); + AnalysisConstraints Value = llvm::StringSwitch<AnalysisConstraints>(Name) +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumConstraints); + if (Value == NumConstraints) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisConstraintsOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_output)) { + StringRef Name = A->getValue(); + AnalysisDiagClients Value = llvm::StringSwitch<AnalysisDiagClients>(Name) +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, PD_##NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NUM_ANALYSIS_DIAG_CLIENTS); + if (Value == NUM_ANALYSIS_DIAG_CLIENTS) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisDiagOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_purge)) { + StringRef Name = A->getValue(); + AnalysisPurgeMode Value = llvm::StringSwitch<AnalysisPurgeMode>(Name) +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumPurgeModes); + if (Value == NumPurgeModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisPurgeOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_inlining_mode)) { + StringRef Name = A->getValue(); + AnalysisInliningMode Value = llvm::StringSwitch<AnalysisInliningMode>(Name) +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumInliningModes); + if (Value == NumInliningModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.InliningMode = Value; + } + } + + Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help); + Opts.DisableAllChecks = Args.hasArg(OPT_analyzer_disable_all_checks); + + Opts.visualizeExplodedGraphWithGraphViz = + Args.hasArg(OPT_analyzer_viz_egraph_graphviz); + Opts.visualizeExplodedGraphWithUbiGraph = + Args.hasArg(OPT_analyzer_viz_egraph_ubigraph); + Opts.NoRetryExhausted = Args.hasArg(OPT_analyzer_disable_retry_exhausted); + Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers); + Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress); + Opts.AnalyzeNestedBlocks = + Args.hasArg(OPT_analyzer_opt_analyze_nested_blocks); + Opts.eagerlyAssumeBinOpBifurcation = Args.hasArg(OPT_analyzer_eagerly_assume); + Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function); + Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG); + Opts.TrimGraph = Args.hasArg(OPT_trim_egraph); + Opts.maxBlockVisitOnPath = + getLastArgIntValue(Args, OPT_analyzer_max_loop, 4, Diags); + Opts.PrintStats = Args.hasArg(OPT_analyzer_stats); + Opts.InlineMaxStackDepth = + getLastArgIntValue(Args, OPT_analyzer_inline_max_stack_depth, + Opts.InlineMaxStackDepth, Diags); + + Opts.CheckersControlList.clear(); + for (arg_iterator it = Args.filtered_begin(OPT_analyzer_checker, + OPT_analyzer_disable_checker), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + A->claim(); + bool enable = (A->getOption().getID() == OPT_analyzer_checker); + // We can have a list of comma separated checker names, e.g: + // '-analyzer-checker=cocoa,unix' + StringRef checkerList = A->getValue(); + SmallVector<StringRef, 4> checkers; + checkerList.split(checkers, ","); + for (unsigned i = 0, e = checkers.size(); i != e; ++i) + Opts.CheckersControlList.push_back(std::make_pair(checkers[i], enable)); + } + + // Go through the analyzer configuration options. + for (arg_iterator it = Args.filtered_begin(OPT_analyzer_config), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + A->claim(); + // We can have a list of comma separated config names, e.g: + // '-analyzer-config key1=val1,key2=val2' + StringRef configList = A->getValue(); + SmallVector<StringRef, 4> configVals; + configList.split(configVals, ","); + for (unsigned i = 0, e = configVals.size(); i != e; ++i) { + StringRef key, val; + std::tie(key, val) = configVals[i].split("="); + if (val.empty()) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_no_value) << configVals[i]; + Success = false; + break; + } + if (val.find('=') != StringRef::npos) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_multiple_values) + << configVals[i]; + Success = false; + break; + } + Opts.Config[key] = val; + } + } + + return Success; +} + +static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { + Opts.NoNSAllocReallocError = Args.hasArg(OPT_migrator_no_nsalloc_error); + Opts.NoFinalizeRemoval = Args.hasArg(OPT_migrator_no_finalize_removal); + return true; +} + +static void ParseCommentArgs(CommentOptions &Opts, ArgList &Args) { + Opts.BlockCommandNames = Args.getAllArgValues(OPT_fcomment_block_commands); + Opts.ParseAllComments = Args.hasArg(OPT_fparse_all_comments); +} + +static StringRef getCodeModel(ArgList &Args, DiagnosticsEngine &Diags) { + if (Arg *A = Args.getLastArg(OPT_mcode_model)) { + StringRef Value = A->getValue(); + if (Value == "small" || Value == "kernel" || Value == "medium" || + Value == "large") + return Value; + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Value; + } + return "default"; +} + +/// \brief Create a new Regex instance out of the string value in \p RpassArg. +/// It returns a pointer to the newly generated Regex instance. +static std::shared_ptr<llvm::Regex> +GenerateOptimizationRemarkRegex(DiagnosticsEngine &Diags, ArgList &Args, + Arg *RpassArg) { + StringRef Val = RpassArg->getValue(); + std::string RegexError; + std::shared_ptr<llvm::Regex> Pattern = std::make_shared<llvm::Regex>(Val); + if (!Pattern->isValid(RegexError)) { + Diags.Report(diag::err_drv_optimization_remark_pattern) + << RegexError << RpassArg->getAsString(Args); + Pattern.reset(); + } + return Pattern; +} + +static void parseSanitizerKinds(StringRef FlagName, + const std::vector<std::string> &Sanitizers, + DiagnosticsEngine &Diags, SanitizerSet &S) { + for (const auto &Sanitizer : Sanitizers) { + SanitizerKind K = llvm::StringSwitch<SanitizerKind>(Sanitizer) +#define SANITIZER(NAME, ID) .Case(NAME, SanitizerKind::ID) +#include "clang/Basic/Sanitizers.def" + .Default(SanitizerKind::Unknown); + if (K == SanitizerKind::Unknown) + Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer; + else + S.set(K, true); + } +} + +static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags, + const TargetOptions &TargetOpts) { + using namespace options; + bool Success = true; + + unsigned OptimizationLevel = getOptimizationLevel(Args, IK, Diags); + // TODO: This could be done in Driver + unsigned MaxOptLevel = 3; + if (OptimizationLevel > MaxOptLevel) { + // If the optimization level is not supported, fall back on the default + // optimization + Diags.Report(diag::warn_drv_optimization_value) + << Args.getLastArg(OPT_O)->getAsString(Args) << "-O" << MaxOptLevel; + OptimizationLevel = MaxOptLevel; + } + Opts.OptimizationLevel = OptimizationLevel; + + // We must always run at least the always inlining pass. + Opts.setInlining( + (Opts.OptimizationLevel > 1) ? CodeGenOptions::NormalInlining + : CodeGenOptions::OnlyAlwaysInlining); + // -fno-inline-functions overrides OptimizationLevel > 1. + Opts.NoInline = Args.hasArg(OPT_fno_inline); + Opts.setInlining(Args.hasArg(OPT_fno_inline_functions) ? + CodeGenOptions::OnlyAlwaysInlining : Opts.getInlining()); + + if (Args.hasArg(OPT_gline_tables_only)) { + Opts.setDebugInfo(CodeGenOptions::DebugLineTablesOnly); + } else if (Args.hasArg(OPT_g_Flag) || Args.hasArg(OPT_gdwarf_2) || + Args.hasArg(OPT_gdwarf_3) || Args.hasArg(OPT_gdwarf_4)) { + bool Default = false; + // Until dtrace (via CTF) and LLDB can deal with distributed debug info, + // Darwin and FreeBSD default to standalone/full debug info. + if (llvm::Triple(TargetOpts.Triple).isOSDarwin() || + llvm::Triple(TargetOpts.Triple).isOSFreeBSD()) + Default = true; + + if (Args.hasFlag(OPT_fstandalone_debug, OPT_fno_standalone_debug, Default)) + Opts.setDebugInfo(CodeGenOptions::FullDebugInfo); + else + Opts.setDebugInfo(CodeGenOptions::LimitedDebugInfo); + } + Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); + Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); + if (Args.hasArg(OPT_gdwarf_2)) + Opts.DwarfVersion = 2; + else if (Args.hasArg(OPT_gdwarf_3)) + Opts.DwarfVersion = 3; + else if (Args.hasArg(OPT_gdwarf_4)) + Opts.DwarfVersion = 4; + else if (Opts.getDebugInfo() != CodeGenOptions::NoDebugInfo) + // Default Dwarf version is 4 if we are generating debug information. + Opts.DwarfVersion = 4; + + Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); + Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); + Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); + Opts.UseRegisterSizedBitfieldAccess = Args.hasArg( + OPT_fuse_register_sized_bitfield_access); + Opts.RelaxedAliasing = Args.hasArg(OPT_relaxed_aliasing); + Opts.StructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa); + Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); + Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants); + Opts.NoCommon = Args.hasArg(OPT_fno_common); + Opts.NoImplicitFloat = Args.hasArg(OPT_no_implicit_float); + Opts.OptimizeSize = getOptimizationLevelSize(Args); + Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) || + Args.hasArg(OPT_ffreestanding)); + Opts.UnrollLoops = + Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops, + (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize)); + Opts.RerollLoops = Args.hasArg(OPT_freroll_loops); + + Opts.DisableIntegratedAS = Args.hasArg(OPT_fno_integrated_as); + Opts.Autolink = !Args.hasArg(OPT_fno_autolink); + Opts.SampleProfileFile = Args.getLastArgValue(OPT_fprofile_sample_use_EQ); + Opts.ProfileInstrGenerate = Args.hasArg(OPT_fprofile_instr_generate); + Opts.InstrProfileInput = Args.getLastArgValue(OPT_fprofile_instr_use_EQ); + Opts.CoverageMapping = Args.hasArg(OPT_fcoverage_mapping); + Opts.DumpCoverageMapping = Args.hasArg(OPT_dump_coverage_mapping); + Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); + Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device); + Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); + Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); + Opts.CodeModel = getCodeModel(Args, Diags); + Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass); + Opts.DisableFPElim = Args.hasArg(OPT_mdisable_fp_elim); + Opts.DisableFree = Args.hasArg(OPT_disable_free); + Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); + Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); + Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable); + Opts.LimitFloatPrecision = Args.getLastArgValue(OPT_mlimit_float_precision); + Opts.NoInfsFPMath = (Args.hasArg(OPT_menable_no_infinities) || + Args.hasArg(OPT_cl_finite_math_only) || + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoNaNsFPMath = (Args.hasArg(OPT_menable_no_nans) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_finite_math_only) || + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoSignedZeros = Args.hasArg(OPT_cl_no_signed_zeros); + Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); + Opts.BackendOptions = Args.getAllArgValues(OPT_backend_option); + Opts.NumRegisterParameters = getLastArgIntValue(Args, OPT_mregparm, 0, Diags); + Opts.NoGlobalMerge = Args.hasArg(OPT_mno_global_merge); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); + Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); + Opts.EnableSegmentedStacks = Args.hasArg(OPT_split_stacks); + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); + Opts.SaveTempLabels = Args.hasArg(OPT_msave_temp_labels); + Opts.NoDwarfDirectoryAsm = Args.hasArg(OPT_fno_dwarf_directory_asm); + Opts.SoftFloat = Args.hasArg(OPT_msoft_float); + Opts.StrictEnums = Args.hasArg(OPT_fstrict_enums); + Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); + Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + Opts.ThreadModel = Args.getLastArgValue(OPT_mthread_model, "posix"); + if (Opts.ThreadModel != "posix" && Opts.ThreadModel != "single") + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_mthread_model)->getAsString(Args) + << Opts.ThreadModel; + Opts.TrapFuncName = Args.getLastArgValue(OPT_ftrap_function_EQ); + Opts.UseInitArray = Args.hasArg(OPT_fuse_init_array); + + Opts.FunctionSections = Args.hasFlag(OPT_ffunction_sections, + OPT_fno_function_sections, false); + Opts.DataSections = Args.hasFlag(OPT_fdata_sections, + OPT_fno_data_sections, false); + Opts.MergeFunctions = Args.hasArg(OPT_fmerge_functions); + + Opts.VectorizeBB = Args.hasArg(OPT_vectorize_slp_aggressive); + Opts.VectorizeLoop = Args.hasArg(OPT_vectorize_loops); + Opts.VectorizeSLP = Args.hasArg(OPT_vectorize_slp); + + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + + Opts.DisableGCov = Args.hasArg(OPT_test_coverage); + Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data); + Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); + if (Opts.EmitGcovArcs || Opts.EmitGcovNotes) { + Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); + Opts.CoverageExtraChecksum = Args.hasArg(OPT_coverage_cfg_checksum); + Opts.CoverageNoFunctionNamesInData = + Args.hasArg(OPT_coverage_no_function_names_in_data); + if (Args.hasArg(OPT_coverage_version_EQ)) { + StringRef CoverageVersion = Args.getLastArgValue(OPT_coverage_version_EQ); + if (CoverageVersion.size() != 4) { + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_coverage_version_EQ)->getAsString(Args) + << CoverageVersion; + } else { + memcpy(Opts.CoverageVersion, CoverageVersion.data(), 4); + } + } + } + + Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); + Opts.EmitOpenCLArgMetadata = Args.hasArg(OPT_cl_kernel_arg_info); + Opts.CompressDebugSections = Args.hasArg(OPT_compress_debug_sections); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file); + Opts.SanitizeCoverage = + getLastArgIntValue(Args, OPT_fsanitize_coverage, 0, Diags); + Opts.SanitizeMemoryTrackOrigins = + getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags); + Opts.SanitizeUndefinedTrapOnError = + Args.hasArg(OPT_fsanitize_undefined_trap_on_error); + Opts.SSPBufferSize = + getLastArgIntValue(Args, OPT_stack_protector_buffer_size, 8, Diags); + Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); + if (Arg *A = Args.getLastArg(OPT_mstack_alignment)) { + StringRef Val = A->getValue(); + unsigned StackAlignment = Opts.StackAlignment; + Val.getAsInteger(10, StackAlignment); + Opts.StackAlignment = StackAlignment; + } + + if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { + StringRef Name = A->getValue(); + unsigned Method = llvm::StringSwitch<unsigned>(Name) + .Case("legacy", CodeGenOptions::Legacy) + .Case("non-legacy", CodeGenOptions::NonLegacy) + .Case("mixed", CodeGenOptions::Mixed) + .Default(~0U); + if (Method == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.setObjCDispatchMethod( + static_cast<CodeGenOptions::ObjCDispatchMethodKind>(Method)); + } + } + + if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) { + StringRef Name = A->getValue(); + unsigned Model = llvm::StringSwitch<unsigned>(Name) + .Case("global-dynamic", CodeGenOptions::GeneralDynamicTLSModel) + .Case("local-dynamic", CodeGenOptions::LocalDynamicTLSModel) + .Case("initial-exec", CodeGenOptions::InitialExecTLSModel) + .Case("local-exec", CodeGenOptions::LocalExecTLSModel) + .Default(~0U); + if (Model == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.setDefaultTLSModel(static_cast<CodeGenOptions::TLSModel>(Model)); + } + } + + if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { + StringRef Val = A->getValue(); + if (Val == "fast") + Opts.setFPContractMode(CodeGenOptions::FPC_Fast); + else if (Val == "on") + Opts.setFPContractMode(CodeGenOptions::FPC_On); + else if (Val == "off") + Opts.setFPContractMode(CodeGenOptions::FPC_Off); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + if (Arg *A = Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return)) { + if (A->getOption().matches(OPT_fpcc_struct_return)) { + Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack); + } else { + assert(A->getOption().matches(OPT_freg_struct_return)); + Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs); + } + } + + Opts.DependentLibraries = Args.getAllArgValues(OPT_dependent_lib); + bool NeedLocTracking = false; + + if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) { + Opts.OptimizationRemarkPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + if (Arg *A = Args.getLastArg(OPT_Rpass_missed_EQ)) { + Opts.OptimizationRemarkMissedPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + if (Arg *A = Args.getLastArg(OPT_Rpass_analysis_EQ)) { + Opts.OptimizationRemarkAnalysisPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + // If the user requested to use a sample profile for PGO, then the + // backend will need to track source location information so the profile + // can be incorporated into the IR. + if (!Opts.SampleProfileFile.empty()) + NeedLocTracking = true; + + // If the user requested a flag that requires source locations available in + // the backend, make sure that the backend tracks source location information. + if (NeedLocTracking && Opts.getDebugInfo() == CodeGenOptions::NoDebugInfo) + Opts.setDebugInfo(CodeGenOptions::LocTrackingOnly); + + Opts.RewriteMapFiles = Args.getAllArgValues(OPT_frewrite_map_file); + + // Parse -fsanitize-recover= arguments. + // FIXME: Report unrecoverable sanitizers incorrectly specified here. + parseSanitizerKinds("-fsanitize-recover=", + Args.getAllArgValues(OPT_fsanitize_recover_EQ), Diags, + Opts.SanitizeRecover); + + return Success; +} + +static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, + ArgList &Args) { + using namespace options; + Opts.OutputFile = Args.getLastArgValue(OPT_dependency_file); + Opts.Targets = Args.getAllArgValues(OPT_MT); + Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); + Opts.IncludeModuleFiles = Args.hasArg(OPT_module_file_deps); + Opts.UsePhonyTargets = Args.hasArg(OPT_MP); + Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); + Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); + Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG); + Opts.PrintShowIncludes = Args.hasArg(OPT_show_includes); + Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot); + Opts.ModuleDependencyOutputDir = + Args.getLastArgValue(OPT_module_dependency_dir); +} + +bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + DiagnosticsEngine *Diags) { + using namespace options; + bool Success = true; + + Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file); + if (Arg *A = + Args.getLastArg(OPT_diagnostic_serialized_file, OPT__serialize_diags)) + Opts.DiagnosticSerializationFile = A->getValue(); + Opts.IgnoreWarnings = Args.hasArg(OPT_w); + Opts.NoRewriteMacros = Args.hasArg(OPT_Wno_rewrite_macros); + Opts.Pedantic = Args.hasArg(OPT_pedantic); + Opts.PedanticErrors = Args.hasArg(OPT_pedantic_errors); + Opts.ShowCarets = !Args.hasArg(OPT_fno_caret_diagnostics); + Opts.ShowColors = Args.hasArg(OPT_fcolor_diagnostics); + Opts.ShowColumn = Args.hasFlag(OPT_fshow_column, + OPT_fno_show_column, + /*Default=*/true); + Opts.ShowFixits = !Args.hasArg(OPT_fno_diagnostics_fixit_info); + Opts.ShowLocation = !Args.hasArg(OPT_fno_show_source_location); + Opts.ShowOptionNames = Args.hasArg(OPT_fdiagnostics_show_option); + + llvm::sys::Process::UseANSIEscapeCodes(Args.hasArg(OPT_fansi_escape_codes)); + + // Default behavior is to not to show note include stacks. + Opts.ShowNoteIncludeStack = false; + if (Arg *A = Args.getLastArg(OPT_fdiagnostics_show_note_include_stack, + OPT_fno_diagnostics_show_note_include_stack)) + if (A->getOption().matches(OPT_fdiagnostics_show_note_include_stack)) + Opts.ShowNoteIncludeStack = true; + + StringRef ShowOverloads = + Args.getLastArgValue(OPT_fshow_overloads_EQ, "all"); + if (ShowOverloads == "best") + Opts.setShowOverloads(Ovl_Best); + else if (ShowOverloads == "all") + Opts.setShowOverloads(Ovl_All); + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fshow_overloads_EQ)->getAsString(Args) + << ShowOverloads; + } + + StringRef ShowCategory = + Args.getLastArgValue(OPT_fdiagnostics_show_category, "none"); + if (ShowCategory == "none") + Opts.ShowCategories = 0; + else if (ShowCategory == "id") + Opts.ShowCategories = 1; + else if (ShowCategory == "name") + Opts.ShowCategories = 2; + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fdiagnostics_show_category)->getAsString(Args) + << ShowCategory; + } + + StringRef Format = + Args.getLastArgValue(OPT_fdiagnostics_format, "clang"); + if (Format == "clang") + Opts.setFormat(DiagnosticOptions::Clang); + else if (Format == "msvc") + Opts.setFormat(DiagnosticOptions::Msvc); + else if (Format == "msvc-fallback") { + Opts.setFormat(DiagnosticOptions::Msvc); + Opts.CLFallbackMode = true; + } else if (Format == "vi") + Opts.setFormat(DiagnosticOptions::Vi); + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fdiagnostics_format)->getAsString(Args) + << Format; + } + + Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); + Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); + Opts.ShowPresumedLoc = !Args.hasArg(OPT_fno_diagnostics_use_presumed_location); + Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.ElideType = !Args.hasArg(OPT_fno_elide_type); + Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree); + Opts.ErrorLimit = getLastArgIntValue(Args, OPT_ferror_limit, 0, Diags); + Opts.MacroBacktraceLimit = + getLastArgIntValue(Args, OPT_fmacro_backtrace_limit, + DiagnosticOptions::DefaultMacroBacktraceLimit, Diags); + Opts.TemplateBacktraceLimit = getLastArgIntValue( + Args, OPT_ftemplate_backtrace_limit, + DiagnosticOptions::DefaultTemplateBacktraceLimit, Diags); + Opts.ConstexprBacktraceLimit = getLastArgIntValue( + Args, OPT_fconstexpr_backtrace_limit, + DiagnosticOptions::DefaultConstexprBacktraceLimit, Diags); + Opts.SpellCheckingLimit = getLastArgIntValue( + Args, OPT_fspell_checking_limit, + DiagnosticOptions::DefaultSpellCheckingLimit, Diags); + Opts.TabStop = getLastArgIntValue(Args, OPT_ftabstop, + DiagnosticOptions::DefaultTabStop, Diags); + if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { + Opts.TabStop = DiagnosticOptions::DefaultTabStop; + if (Diags) + Diags->Report(diag::warn_ignoring_ftabstop_value) + << Opts.TabStop << DiagnosticOptions::DefaultTabStop; + } + Opts.MessageLength = getLastArgIntValue(Args, OPT_fmessage_length, 0, Diags); + addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); + addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); + + return Success; +} + +static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { + Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory); +} + +static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + using namespace options; + Opts.ProgramAction = frontend::ParseSyntaxOnly; + if (const Arg *A = Args.getLastArg(OPT_Action_Group)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("Invalid option in group!"); + case OPT_ast_list: + Opts.ProgramAction = frontend::ASTDeclList; break; + case OPT_ast_dump: + case OPT_ast_dump_lookups: + Opts.ProgramAction = frontend::ASTDump; break; + case OPT_ast_print: + Opts.ProgramAction = frontend::ASTPrint; break; + case OPT_ast_view: + Opts.ProgramAction = frontend::ASTView; break; + case OPT_dump_raw_tokens: + Opts.ProgramAction = frontend::DumpRawTokens; break; + case OPT_dump_tokens: + Opts.ProgramAction = frontend::DumpTokens; break; + case OPT_S: + Opts.ProgramAction = frontend::EmitAssembly; break; + case OPT_emit_llvm_bc: + Opts.ProgramAction = frontend::EmitBC; break; + case OPT_emit_html: + Opts.ProgramAction = frontend::EmitHTML; break; + case OPT_emit_llvm: + Opts.ProgramAction = frontend::EmitLLVM; break; + case OPT_emit_llvm_only: + Opts.ProgramAction = frontend::EmitLLVMOnly; break; + case OPT_emit_codegen_only: + Opts.ProgramAction = frontend::EmitCodeGenOnly; break; + case OPT_emit_obj: + Opts.ProgramAction = frontend::EmitObj; break; + case OPT_fixit_EQ: + Opts.FixItSuffix = A->getValue(); + // fall-through! + case OPT_fixit: + Opts.ProgramAction = frontend::FixIt; break; + case OPT_emit_module: + Opts.ProgramAction = frontend::GenerateModule; break; + case OPT_emit_pch: + Opts.ProgramAction = frontend::GeneratePCH; break; + case OPT_emit_pth: + Opts.ProgramAction = frontend::GeneratePTH; break; + case OPT_init_only: + Opts.ProgramAction = frontend::InitOnly; break; + case OPT_fsyntax_only: + Opts.ProgramAction = frontend::ParseSyntaxOnly; break; + case OPT_module_file_info: + Opts.ProgramAction = frontend::ModuleFileInfo; break; + case OPT_verify_pch: + Opts.ProgramAction = frontend::VerifyPCH; break; + case OPT_print_decl_contexts: + Opts.ProgramAction = frontend::PrintDeclContext; break; + case OPT_print_preamble: + Opts.ProgramAction = frontend::PrintPreamble; break; + case OPT_E: + Opts.ProgramAction = frontend::PrintPreprocessedInput; break; + case OPT_rewrite_macros: + Opts.ProgramAction = frontend::RewriteMacros; break; + case OPT_rewrite_objc: + Opts.ProgramAction = frontend::RewriteObjC; break; + case OPT_rewrite_test: + Opts.ProgramAction = frontend::RewriteTest; break; + case OPT_analyze: + Opts.ProgramAction = frontend::RunAnalysis; break; + case OPT_migrate: + Opts.ProgramAction = frontend::MigrateSource; break; + case OPT_Eonly: + Opts.ProgramAction = frontend::RunPreprocessorOnly; break; + } + } + + if (const Arg* A = Args.getLastArg(OPT_plugin)) { + Opts.Plugins.push_back(A->getValue(0)); + Opts.ProgramAction = frontend::PluginAction; + Opts.ActionName = A->getValue(); + + for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg), + end = Args.filtered_end(); it != end; ++it) { + if ((*it)->getValue(0) == Opts.ActionName) + Opts.PluginArgs.push_back((*it)->getValue(1)); + } + } + + Opts.AddPluginActions = Args.getAllArgValues(OPT_add_plugin); + Opts.AddPluginArgs.resize(Opts.AddPluginActions.size()); + for (int i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) { + for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg), + end = Args.filtered_end(); it != end; ++it) { + if ((*it)->getValue(0) == Opts.AddPluginActions[i]) + Opts.AddPluginArgs[i].push_back((*it)->getValue(1)); + } + } + + if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) { + Opts.CodeCompletionAt = + ParsedSourceLocation::FromString(A->getValue()); + if (Opts.CodeCompletionAt.FileName.empty()) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + Opts.DisableFree = Args.hasArg(OPT_disable_free); + + Opts.OutputFile = Args.getLastArgValue(OPT_o); + Opts.Plugins = Args.getAllArgValues(OPT_load); + Opts.RelocatablePCH = Args.hasArg(OPT_relocatable_pch); + Opts.ShowHelp = Args.hasArg(OPT_help); + Opts.ShowStats = Args.hasArg(OPT_print_stats); + Opts.ShowTimers = Args.hasArg(OPT_ftime_report); + Opts.ShowVersion = Args.hasArg(OPT_version); + Opts.ASTMergeFiles = Args.getAllArgValues(OPT_ast_merge); + Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); + Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can); + Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings); + Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile); + Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); + Opts.ASTDumpDecls = Args.hasArg(OPT_ast_dump); + Opts.ASTDumpFilter = Args.getLastArgValue(OPT_ast_dump_filter); + Opts.ASTDumpLookups = Args.hasArg(OPT_ast_dump_lookups); + Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index); + Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex; + Opts.ModuleMapFiles = Args.getAllArgValues(OPT_fmodule_map_file); + Opts.ModuleFiles = Args.getAllArgValues(OPT_fmodule_file); + + Opts.CodeCompleteOpts.IncludeMacros + = Args.hasArg(OPT_code_completion_macros); + Opts.CodeCompleteOpts.IncludeCodePatterns + = Args.hasArg(OPT_code_completion_patterns); + Opts.CodeCompleteOpts.IncludeGlobals + = !Args.hasArg(OPT_no_code_completion_globals); + Opts.CodeCompleteOpts.IncludeBriefComments + = Args.hasArg(OPT_code_completion_brief_comments); + + Opts.OverrideRecordLayoutsFile + = Args.getLastArgValue(OPT_foverride_record_layout_EQ); + if (const Arg *A = Args.getLastArg(OPT_arcmt_check, + OPT_arcmt_modify, + OPT_arcmt_migrate)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("missed a case"); + case OPT_arcmt_check: + Opts.ARCMTAction = FrontendOptions::ARCMT_Check; + break; + case OPT_arcmt_modify: + Opts.ARCMTAction = FrontendOptions::ARCMT_Modify; + break; + case OPT_arcmt_migrate: + Opts.ARCMTAction = FrontendOptions::ARCMT_Migrate; + break; + } + } + Opts.MTMigrateDir = Args.getLastArgValue(OPT_mt_migrate_directory); + Opts.ARCMTMigrateReportOut + = Args.getLastArgValue(OPT_arcmt_migrate_report_output); + Opts.ARCMTMigrateEmitARCErrors + = Args.hasArg(OPT_arcmt_migrate_emit_arc_errors); + + if (Args.hasArg(OPT_objcmt_migrate_literals)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Literals; + if (Args.hasArg(OPT_objcmt_migrate_subscripting)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Subscripting; + if (Args.hasArg(OPT_objcmt_migrate_property_dot_syntax)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_PropertyDotSyntax; + if (Args.hasArg(OPT_objcmt_migrate_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Property; + if (Args.hasArg(OPT_objcmt_migrate_readonly_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReadonlyProperty; + if (Args.hasArg(OPT_objcmt_migrate_readwrite_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReadwriteProperty; + if (Args.hasArg(OPT_objcmt_migrate_annotation)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Annotation; + if (Args.hasArg(OPT_objcmt_returns_innerpointer_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReturnsInnerPointerProperty; + if (Args.hasArg(OPT_objcmt_migrate_instancetype)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Instancetype; + if (Args.hasArg(OPT_objcmt_migrate_nsmacros)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_NsMacros; + if (Args.hasArg(OPT_objcmt_migrate_protocol_conformance)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ProtocolConformance; + if (Args.hasArg(OPT_objcmt_atomic_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_AtomicProperty; + if (Args.hasArg(OPT_objcmt_ns_nonatomic_iosonly)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty; + if (Args.hasArg(OPT_objcmt_migrate_designated_init)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_DesignatedInitializer; + if (Args.hasArg(OPT_objcmt_migrate_all)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_MigrateDecls; + + Opts.ObjCMTWhiteListPath = Args.getLastArgValue(OPT_objcmt_whitelist_dir_path); + + if (Opts.ARCMTAction != FrontendOptions::ARCMT_None && + Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "ARC migration" << "ObjC migration"; + } + + InputKind DashX = IK_None; + if (const Arg *A = Args.getLastArg(OPT_x)) { + DashX = llvm::StringSwitch<InputKind>(A->getValue()) + .Case("c", IK_C) + .Case("cl", IK_OpenCL) + .Case("cuda", IK_CUDA) + .Case("c++", IK_CXX) + .Case("objective-c", IK_ObjC) + .Case("objective-c++", IK_ObjCXX) + .Case("cpp-output", IK_PreprocessedC) + .Case("assembler-with-cpp", IK_Asm) + .Case("c++-cpp-output", IK_PreprocessedCXX) + .Case("objective-c-cpp-output", IK_PreprocessedObjC) + .Case("objc-cpp-output", IK_PreprocessedObjC) + .Case("objective-c++-cpp-output", IK_PreprocessedObjCXX) + .Case("objc++-cpp-output", IK_PreprocessedObjCXX) + .Case("c-header", IK_C) + .Case("cl-header", IK_OpenCL) + .Case("objective-c-header", IK_ObjC) + .Case("c++-header", IK_CXX) + .Case("objective-c++-header", IK_ObjCXX) + .Cases("ast", "pcm", IK_AST) + .Case("ir", IK_LLVM_IR) + .Default(IK_None); + if (DashX == IK_None) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + + // '-' is the default input if none is given. + std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT); + Opts.Inputs.clear(); + if (Inputs.empty()) + Inputs.push_back("-"); + for (unsigned i = 0, e = Inputs.size(); i != e; ++i) { + InputKind IK = DashX; + if (IK == IK_None) { + IK = FrontendOptions::getInputKindForExtension( + StringRef(Inputs[i]).rsplit('.').second); + // FIXME: Remove this hack. + if (i == 0) + DashX = IK; + } + Opts.Inputs.push_back(FrontendInputFile(Inputs[i], IK)); + } + + return DashX; +} + +std::string CompilerInvocation::GetResourcesPath(const char *Argv0, + void *MainAddr) { + std::string ClangExecutable = + llvm::sys::fs::getMainExecutable(Argv0, MainAddr); + StringRef Dir = llvm::sys::path::parent_path(ClangExecutable); + + // Compute the path to the resource directory. + StringRef ClangResourceDir(CLANG_RESOURCE_DIR); + SmallString<128> P(Dir); + if (ClangResourceDir != "") { + llvm::sys::path::append(P, ClangResourceDir); + } else { + StringRef ClangLibdirSuffix(CLANG_LIBDIR_SUFFIX); + llvm::sys::path::append(P, "..", Twine("lib") + ClangLibdirSuffix, "clang", + CLANG_VERSION_STRING); + } + + return P.str(); +} + +static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { + using namespace options; + Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/"); + Opts.Verbose = Args.hasArg(OPT_v); + Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc); + Opts.UseStandardSystemIncludes = !Args.hasArg(OPT_nostdsysteminc); + Opts.UseStandardCXXIncludes = !Args.hasArg(OPT_nostdincxx); + if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ)) + Opts.UseLibcxx = (strcmp(A->getValue(), "libc++") == 0); + Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir); + Opts.ModuleCachePath = Args.getLastArgValue(OPT_fmodules_cache_path); + Opts.ModuleUserBuildPath = Args.getLastArgValue(OPT_fmodules_user_build_path); + Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); + // -fmodules implies -fmodule-maps + Opts.ModuleMaps = Args.hasArg(OPT_fmodule_maps) || Args.hasArg(OPT_fmodules); + Opts.ModuleMapFileHomeIsCwd = Args.hasArg(OPT_fmodule_map_file_home_is_cwd); + Opts.ModuleCachePruneInterval = + getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60); + Opts.ModuleCachePruneAfter = + getLastArgIntValue(Args, OPT_fmodules_prune_after, 31 * 24 * 60 * 60); + Opts.ModulesValidateOncePerBuildSession = + Args.hasArg(OPT_fmodules_validate_once_per_build_session); + Opts.BuildSessionTimestamp = + getLastArgUInt64Value(Args, OPT_fbuild_session_timestamp, 0); + Opts.ModulesValidateSystemHeaders = + Args.hasArg(OPT_fmodules_validate_system_headers); + + for (arg_iterator it = Args.filtered_begin(OPT_fmodules_ignore_macro), + ie = Args.filtered_end(); + it != ie; ++it) { + StringRef MacroDef = (*it)->getValue(); + Opts.ModulesIgnoreMacros.insert(MacroDef.split('=').first); + } + + // Add -I..., -F..., and -index-header-map options in order. + bool IsIndexHeaderMap = false; + for (arg_iterator it = Args.filtered_begin(OPT_I, OPT_F, + OPT_index_header_map), + ie = Args.filtered_end(); it != ie; ++it) { + if ((*it)->getOption().matches(OPT_index_header_map)) { + // -index-header-map applies to the next -I or -F. + IsIndexHeaderMap = true; + continue; + } + + frontend::IncludeDirGroup Group + = IsIndexHeaderMap? frontend::IndexHeaderMap : frontend::Angled; + + Opts.AddPath((*it)->getValue(), Group, + /*IsFramework=*/ (*it)->getOption().matches(OPT_F), true); + IsIndexHeaderMap = false; + } + + // Add -iprefix/-iwithprefix/-iwithprefixbefore options. + StringRef Prefix = ""; // FIXME: This isn't the correct default prefix. + for (arg_iterator it = Args.filtered_begin(OPT_iprefix, OPT_iwithprefix, + OPT_iwithprefixbefore), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(OPT_iprefix)) + Prefix = A->getValue(); + else if (A->getOption().matches(OPT_iwithprefix)) + Opts.AddPath(Prefix.str() + A->getValue(), + frontend::After, false, true); + else + Opts.AddPath(Prefix.str() + A->getValue(), + frontend::Angled, false, true); + } + + for (arg_iterator it = Args.filtered_begin(OPT_idirafter), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::After, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_iquote), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::Quoted, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_isystem, + OPT_iwithsysroot), ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::System, false, + !(*it)->getOption().matches(OPT_iwithsysroot)); + for (arg_iterator it = Args.filtered_begin(OPT_iframework), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::System, true, true); + + // Add the paths for the various language specific isystem flags. + for (arg_iterator it = Args.filtered_begin(OPT_c_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::CSystem, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_cxx_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::CXXSystem, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_objc_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::ObjCSystem, false,true); + for (arg_iterator it = Args.filtered_begin(OPT_objcxx_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::ObjCXXSystem, false, true); + + // Add the internal paths from a driver that detects standard include paths. + for (arg_iterator I = Args.filtered_begin(OPT_internal_isystem, + OPT_internal_externc_isystem), + E = Args.filtered_end(); + I != E; ++I) { + frontend::IncludeDirGroup Group = frontend::System; + if ((*I)->getOption().matches(OPT_internal_externc_isystem)) + Group = frontend::ExternCSystem; + Opts.AddPath((*I)->getValue(), Group, false, true); + } + + // Add the path prefixes which are implicitly treated as being system headers. + for (arg_iterator I = Args.filtered_begin(OPT_system_header_prefix, + OPT_no_system_header_prefix), + E = Args.filtered_end(); + I != E; ++I) + Opts.AddSystemHeaderPrefix( + (*I)->getValue(), (*I)->getOption().matches(OPT_system_header_prefix)); + + for (arg_iterator I = Args.filtered_begin(OPT_ivfsoverlay), + E = Args.filtered_end(); I != E; ++I) + Opts.AddVFSOverlayFile((*I)->getValue()); +} + +void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, + LangStandard::Kind LangStd) { + // Set some properties which depend solely on the input kind; it would be nice + // to move these to the language standard, and have the driver resolve the + // input kind + language standard. + if (IK == IK_Asm) { + Opts.AsmPreprocessor = 1; + } else if (IK == IK_ObjC || + IK == IK_ObjCXX || + IK == IK_PreprocessedObjC || + IK == IK_PreprocessedObjCXX) { + Opts.ObjC1 = Opts.ObjC2 = 1; + } + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK) { + case IK_None: + case IK_AST: + case IK_LLVM_IR: + llvm_unreachable("Invalid input kind!"); + case IK_OpenCL: + LangStd = LangStandard::lang_opencl; + break; + case IK_CUDA: + LangStd = LangStandard::lang_cuda; + break; + case IK_Asm: + case IK_C: + case IK_PreprocessedC: + case IK_ObjC: + case IK_PreprocessedObjC: + LangStd = LangStandard::lang_gnu11; + break; + case IK_CXX: + case IK_PreprocessedCXX: + case IK_ObjCXX: + case IK_PreprocessedObjCXX: + LangStd = LangStandard::lang_gnucxx98; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.LineComment = Std.hasLineComments(); + Opts.C99 = Std.isC99(); + Opts.C11 = Std.isC11(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus11 = Std.isCPlusPlus11(); + Opts.CPlusPlus14 = Std.isCPlusPlus14(); + Opts.CPlusPlus1z = Std.isCPlusPlus1z(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Std.isC99(); + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + // Set OpenCL Version. + Opts.OpenCL = LangStd == LangStandard::lang_opencl || IK == IK_OpenCL; + if (LangStd == LangStandard::lang_opencl) + Opts.OpenCLVersion = 100; + else if (LangStd == LangStandard::lang_opencl11) + Opts.OpenCLVersion = 110; + else if (LangStd == LangStandard::lang_opencl12) + Opts.OpenCLVersion = 120; + else if (LangStd == LangStandard::lang_opencl20) + Opts.OpenCLVersion = 200; + + // OpenCL has some additional defaults. + if (Opts.OpenCL) { + Opts.AltiVec = 0; + Opts.CXXOperatorNames = 1; + Opts.LaxVectorConversions = 0; + Opts.DefaultFPContract = 1; + Opts.NativeHalfType = 1; + } + + Opts.CUDA = LangStd == LangStandard::lang_cuda || IK == IK_CUDA; + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + + // OpenCL has half keyword + Opts.Half = Opts.OpenCL; + + // C++ has wchar_t keyword. + Opts.WChar = Opts.CPlusPlus; + + Opts.GNUKeywords = Opts.GNUMode; + Opts.CXXOperatorNames = Opts.CPlusPlus; + + Opts.DollarIdents = !Opts.AsmPreprocessor; + + // C++14 onwards has sized global deallocation functions. + Opts.SizedDeallocation = Opts.CPlusPlus14; +} + +/// Attempt to parse a visibility value out of the given argument. +static Visibility parseVisibility(Arg *arg, ArgList &args, + DiagnosticsEngine &diags) { + StringRef value = arg->getValue(); + if (value == "default") { + return DefaultVisibility; + } else if (value == "hidden") { + return HiddenVisibility; + } else if (value == "protected") { + // FIXME: diagnose if target does not support protected visibility + return ProtectedVisibility; + } + + diags.Report(diag::err_drv_invalid_value) + << arg->getAsString(args) << value; + return DefaultVisibility; +} + +static unsigned parseMSCVersion(ArgList &Args, DiagnosticsEngine &Diags) { + auto Arg = Args.getLastArg(OPT_fms_compatibility_version); + if (!Arg) + return 0; + + // The MSC versioning scheme involves four versioning components: + // - Major + // - Minor + // - Build + // - Patch + // + // We accept either the old style (_MSC_VER) value, or a _MSC_FULL_VER value. + // Additionally, the value may be provided in the form of a more readable + // MM.mm.bbbbb.pp version. + // + // Unfortunately, due to the bit-width limitations, we cannot currently encode + // the value for the patch level. + + unsigned VC[4] = {0}; + StringRef Value = Arg->getValue(); + SmallVector<StringRef, 4> Components; + + Value.split(Components, ".", llvm::array_lengthof(VC)); + for (unsigned CI = 0, + CE = std::min(Components.size(), llvm::array_lengthof(VC)); + CI < CE; ++CI) { + if (Components[CI].getAsInteger(10, VC[CI])) { + Diags.Report(diag::err_drv_invalid_value) + << Arg->getAsString(Args) << Value; + return 0; + } + } + + // FIXME we cannot encode the patch level + return VC[0] * 10000000 + VC[1] * 100000 + VC[2]; +} + +static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + // FIXME: Cleanup per-file based stuff. + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + if (const Arg *A = Args.getLastArg(OPT_std_EQ)) { + LangStd = llvm::StringSwitch<LangStandard::Kind>(A->getValue()) +#define LANGSTANDARD(id, name, desc, features) \ + .Case(name, LangStandard::lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(LangStandard::lang_unspecified); + if (LangStd == LangStandard::lang_unspecified) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + else { + // Valid standard, check to make sure language and standard are + // compatible. + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + switch (IK) { + case IK_C: + case IK_ObjC: + case IK_PreprocessedC: + case IK_PreprocessedObjC: + if (!(Std.isC89() || Std.isC99())) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "C/ObjC"; + break; + case IK_CXX: + case IK_ObjCXX: + case IK_PreprocessedCXX: + case IK_PreprocessedObjCXX: + if (!Std.isCPlusPlus()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "C++/ObjC++"; + break; + case IK_OpenCL: + if (!Std.isC99()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "OpenCL"; + break; + case IK_CUDA: + if (!Std.isCPlusPlus()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "CUDA"; + break; + default: + break; + } + } + } + + // -cl-std only applies for OpenCL language standards. + // Override the -std option in this case. + if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) { + LangStandard::Kind OpenCLLangStd + = llvm::StringSwitch<LangStandard::Kind>(A->getValue()) + .Case("CL", LangStandard::lang_opencl) + .Case("CL1.1", LangStandard::lang_opencl11) + .Case("CL1.2", LangStandard::lang_opencl12) + .Case("CL2.0", LangStandard::lang_opencl20) + .Default(LangStandard::lang_unspecified); + + if (OpenCLLangStd == LangStandard::lang_unspecified) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + else + LangStd = OpenCLLangStd; + } + + CompilerInvocation::setLangDefaults(Opts, IK, LangStd); + + // We abuse '-f[no-]gnu-keywords' to force overriding all GNU-extension + // keywords. This behavior is provided by GCC's poorly named '-fasm' flag, + // while a subset (the non-C++ GNU keywords) is provided by GCC's + // '-fgnu-keywords'. Clang conflates the two for simplicity under the single + // name, as it doesn't seem a useful distinction. + Opts.GNUKeywords = Args.hasFlag(OPT_fgnu_keywords, OPT_fno_gnu_keywords, + Opts.GNUKeywords); + + if (Args.hasArg(OPT_fno_operator_names)) + Opts.CXXOperatorNames = 0; + + if (Args.hasArg(OPT_fcuda_is_device)) + Opts.CUDAIsDevice = 1; + + if (Opts.ObjC1) { + if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) { + StringRef value = arg->getValue(); + if (Opts.ObjCRuntime.tryParse(value)) + Diags.Report(diag::err_drv_unknown_objc_runtime) << value; + } + + if (Args.hasArg(OPT_fobjc_gc_only)) + Opts.setGC(LangOptions::GCOnly); + else if (Args.hasArg(OPT_fobjc_gc)) + Opts.setGC(LangOptions::HybridGC); + else if (Args.hasArg(OPT_fobjc_arc)) { + Opts.ObjCAutoRefCount = 1; + if (!Opts.ObjCRuntime.allowsARC()) + Diags.Report(diag::err_arc_unsupported_on_runtime); + + // Only set ObjCARCWeak if ARC is enabled. + if (Args.hasArg(OPT_fobjc_runtime_has_weak)) + Opts.ObjCARCWeak = 1; + else + Opts.ObjCARCWeak = Opts.ObjCRuntime.allowsWeak(); + } + + if (Args.hasArg(OPT_fno_objc_infer_related_result_type)) + Opts.ObjCInferRelatedResultType = 0; + + if (Args.hasArg(OPT_fobjc_subscripting_legacy_runtime)) + Opts.ObjCSubscriptingLegacyRuntime = + (Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX); + } + + if (Args.hasArg(OPT_fgnu89_inline)) + Opts.GNUInline = 1; + + if (Args.hasArg(OPT_fapple_kext)) { + if (!Opts.CPlusPlus) + Diags.Report(diag::warn_c_kext); + else + Opts.AppleKext = 1; + } + + if (Args.hasArg(OPT_print_ivar_layout)) + Opts.ObjCGCBitmapPrint = 1; + if (Args.hasArg(OPT_fno_constant_cfstrings)) + Opts.NoConstantCFStrings = 1; + + if (Args.hasArg(OPT_faltivec)) + Opts.AltiVec = 1; + + if (Args.hasArg(OPT_pthread)) + Opts.POSIXThreads = 1; + + // The value-visibility mode defaults to "default". + if (Arg *visOpt = Args.getLastArg(OPT_fvisibility)) { + Opts.setValueVisibilityMode(parseVisibility(visOpt, Args, Diags)); + } else { + Opts.setValueVisibilityMode(DefaultVisibility); + } + + // The type-visibility mode defaults to the value-visibility mode. + if (Arg *typeVisOpt = Args.getLastArg(OPT_ftype_visibility)) { + Opts.setTypeVisibilityMode(parseVisibility(typeVisOpt, Args, Diags)); + } else { + Opts.setTypeVisibilityMode(Opts.getValueVisibilityMode()); + } + + if (Args.hasArg(OPT_fvisibility_inlines_hidden)) + Opts.InlineVisibilityHidden = 1; + + if (Args.hasArg(OPT_ftrapv)) { + Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping); + // Set the handler, if one is specified. + Opts.OverflowHandler = + Args.getLastArgValue(OPT_ftrapv_handler); + } + else if (Args.hasArg(OPT_fwrapv)) + Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined); + + Opts.MSVCCompat = Args.hasArg(OPT_fms_compatibility); + Opts.MicrosoftExt = Opts.MSVCCompat || Args.hasArg(OPT_fms_extensions); + Opts.AsmBlocks = Args.hasArg(OPT_fasm_blocks) || Opts.MicrosoftExt; + Opts.MSCompatibilityVersion = parseMSCVersion(Args, Diags); + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + // Trigraphs are disabled by default in c++1z onwards. + Opts.Trigraphs = !Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus1z; + Opts.Trigraphs = + Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs); + + Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, + OPT_fno_dollars_in_identifiers, + Opts.DollarIdents); + Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); + Opts.VtorDispMode = getLastArgIntValue(Args, OPT_vtordisp_mode_EQ, 1, Diags); + Opts.Borland = Args.hasArg(OPT_fborland_extensions); + Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); + Opts.ConstStrings = Args.hasFlag(OPT_fconst_strings, OPT_fno_const_strings, + Opts.ConstStrings); + if (Args.hasArg(OPT_fno_lax_vector_conversions)) + Opts.LaxVectorConversions = 0; + if (Args.hasArg(OPT_fno_threadsafe_statics)) + Opts.ThreadsafeStatics = 0; + Opts.Exceptions = Args.hasArg(OPT_fexceptions); + Opts.ObjCExceptions = Args.hasArg(OPT_fobjc_exceptions); + Opts.CXXExceptions = Args.hasArg(OPT_fcxx_exceptions); + Opts.SjLjExceptions = Args.hasArg(OPT_fsjlj_exceptions); + Opts.TraditionalCPP = Args.hasArg(OPT_traditional_cpp); + + Opts.RTTI = !Args.hasArg(OPT_fno_rtti); + Opts.RTTIData = Opts.RTTI && !Args.hasArg(OPT_fno_rtti_data); + Opts.Blocks = Args.hasArg(OPT_fblocks); + Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional); + Opts.Modules = Args.hasArg(OPT_fmodules); + Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse); + Opts.ModulesDeclUse = + Args.hasArg(OPT_fmodules_decluse) || Opts.ModulesStrictDeclUse; + Opts.ModulesSearchAll = Opts.Modules && + !Args.hasArg(OPT_fno_modules_search_all) && + Args.hasArg(OPT_fmodules_search_all); + Opts.ModulesErrorRecovery = !Args.hasArg(OPT_fno_modules_error_recovery); + Opts.ModulesImplicitMaps = Args.hasFlag(OPT_fmodules_implicit_maps, + OPT_fno_modules_implicit_maps, true); + Opts.CharIsSigned = Opts.OpenCL || !Args.hasArg(OPT_fno_signed_char); + Opts.WChar = Opts.CPlusPlus && !Args.hasArg(OPT_fno_wchar); + Opts.ShortWChar = Args.hasFlag(OPT_fshort_wchar, OPT_fno_short_wchar, false); + Opts.ShortEnums = Args.hasArg(OPT_fshort_enums); + Opts.Freestanding = Args.hasArg(OPT_ffreestanding); + Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; + Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin); + Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); + Opts.SizedDeallocation |= Args.hasArg(OPT_fsized_deallocation); + Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); + Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); + Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); + Opts.MathErrno = !Opts.OpenCL && Args.hasArg(OPT_fmath_errno); + Opts.InstantiationDepth = + getLastArgIntValue(Args, OPT_ftemplate_depth, 256, Diags); + Opts.ArrowDepth = + getLastArgIntValue(Args, OPT_foperator_arrow_depth, 256, Diags); + Opts.ConstexprCallDepth = + getLastArgIntValue(Args, OPT_fconstexpr_depth, 512, Diags); + Opts.ConstexprStepLimit = + getLastArgIntValue(Args, OPT_fconstexpr_steps, 1048576, Diags); + Opts.BracketDepth = getLastArgIntValue(Args, OPT_fbracket_depth, 256, Diags); + Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); + Opts.NumLargeByValueCopy = + getLastArgIntValue(Args, OPT_Wlarge_by_value_copy_EQ, 0, Diags); + Opts.MSBitfields = Args.hasArg(OPT_mms_bitfields); + Opts.ObjCConstantStringClass = + Args.getLastArgValue(OPT_fconstant_string_class); + Opts.ObjCDefaultSynthProperties = + !Args.hasArg(OPT_disable_objc_default_synthesize_properties); + Opts.EncodeExtendedBlockSig = + Args.hasArg(OPT_fencode_extended_block_signature); + Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); + Opts.PackStruct = getLastArgIntValue(Args, OPT_fpack_struct_EQ, 0, Diags); + Opts.MaxTypeAlign = getLastArgIntValue(Args, OPT_fmax_type_align_EQ, 0, Diags); + Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); + Opts.PIELevel = getLastArgIntValue(Args, OPT_pie_level, 0, Diags); + Opts.Static = Args.hasArg(OPT_static_define); + Opts.DumpRecordLayoutsSimple = Args.hasArg(OPT_fdump_record_layouts_simple); + Opts.DumpRecordLayouts = Opts.DumpRecordLayoutsSimple + || Args.hasArg(OPT_fdump_record_layouts); + Opts.DumpVTableLayouts = Args.hasArg(OPT_fdump_vtable_layouts); + Opts.SpellChecking = !Args.hasArg(OPT_fno_spell_checking); + Opts.NoBitFieldTypeAlign = Args.hasArg(OPT_fno_bitfield_type_align); + Opts.SinglePrecisionConstants = Args.hasArg(OPT_cl_single_precision_constant); + Opts.FastRelaxedMath = Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.MRTD = Args.hasArg(OPT_mrtd); + Opts.HexagonQdsp6Compat = Args.hasArg(OPT_mqdsp6_compat); + Opts.FakeAddressSpaceMap = Args.hasArg(OPT_ffake_address_space_map); + Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype); + Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support); + Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id); + Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal); + Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack); + Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name); + Opts.ImplementationOfModule = + Args.getLastArgValue(OPT_fmodule_implementation_of); + Opts.NativeHalfType = Opts.NativeHalfType; + Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns); + + if (!Opts.CurrentModule.empty() && !Opts.ImplementationOfModule.empty() && + Opts.CurrentModule != Opts.ImplementationOfModule) { + Diags.Report(diag::err_conflicting_module_names) + << Opts.CurrentModule << Opts.ImplementationOfModule; + } + + if (Arg *A = Args.getLastArg(OPT_faddress_space_map_mangling_EQ)) { + switch (llvm::StringSwitch<unsigned>(A->getValue()) + .Case("target", LangOptions::ASMM_Target) + .Case("no", LangOptions::ASMM_Off) + .Case("yes", LangOptions::ASMM_On) + .Default(255)) { + default: + Diags.Report(diag::err_drv_invalid_value) + << "-faddress-space-map-mangling=" << A->getValue(); + break; + case LangOptions::ASMM_Target: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_Target); + break; + case LangOptions::ASMM_On: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_On); + break; + case LangOptions::ASMM_Off: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_Off); + break; + } + } + + if (Arg *A = Args.getLastArg(OPT_fms_memptr_rep_EQ)) { + LangOptions::PragmaMSPointersToMembersKind InheritanceModel = + llvm::StringSwitch<LangOptions::PragmaMSPointersToMembersKind>( + A->getValue()) + .Case("single", + LangOptions::PPTMK_FullGeneralitySingleInheritance) + .Case("multiple", + LangOptions::PPTMK_FullGeneralityMultipleInheritance) + .Case("virtual", + LangOptions::PPTMK_FullGeneralityVirtualInheritance) + .Default(LangOptions::PPTMK_BestCase); + if (InheritanceModel == LangOptions::PPTMK_BestCase) + Diags.Report(diag::err_drv_invalid_value) + << "-fms-memptr-rep=" << A->getValue(); + + Opts.setMSPointerToMemberRepresentationMethod(InheritanceModel); + } + + // Check if -fopenmp= is specified. + if (const Arg *A = Args.getLastArg(options::OPT_fopenmp_EQ)) { + Opts.OpenMP = llvm::StringSwitch<bool>(A->getValue()) + .Case("libiomp5", true) + .Default(false); + } + + // Record whether the __DEPRECATED define was requested. + Opts.Deprecated = Args.hasFlag(OPT_fdeprecated_macro, + OPT_fno_deprecated_macro, + Opts.Deprecated); + + // FIXME: Eliminate this dependency. + unsigned Opt = getOptimizationLevel(Args, IK, Diags), + OptSize = getOptimizationLevelSize(Args); + Opts.Optimize = Opt != 0; + Opts.OptimizeSize = OptSize != 0; + + // This is the __NO_INLINE__ define, which just depends on things like the + // optimization level and -fno-inline, not actually whether the backend has + // inlining enabled. + Opts.NoInlineDefine = !Opt || Args.hasArg(OPT_fno_inline); + + Opts.FastMath = Args.hasArg(OPT_ffast_math) || + Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only) || + Args.hasArg(OPT_cl_finite_math_only) || + Args.hasArg(OPT_cl_fast_relaxed_math); + + Opts.RetainCommentsFromSystemHeaders = + Args.hasArg(OPT_fretain_comments_from_system_headers); + + unsigned SSP = getLastArgIntValue(Args, OPT_stack_protector, 0, Diags); + switch (SSP) { + default: + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << SSP; + break; + case 0: Opts.setStackProtector(LangOptions::SSPOff); break; + case 1: Opts.setStackProtector(LangOptions::SSPOn); break; + case 2: Opts.setStackProtector(LangOptions::SSPStrong); break; + case 3: Opts.setStackProtector(LangOptions::SSPReq); break; + } + + // Parse -fsanitize= arguments. + parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), + Diags, Opts.Sanitize); + // -fsanitize-address-field-padding=N has to be a LangOpt, parse it here. + Opts.SanitizeAddressFieldPadding = + getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags); + Opts.SanitizerBlacklistFile = Args.getLastArgValue(OPT_fsanitize_blacklist); +} + +static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, + FileManager &FileMgr, + DiagnosticsEngine &Diags) { + using namespace options; + Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch); + Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth); + if (const Arg *A = Args.getLastArg(OPT_token_cache)) + Opts.TokenCache = A->getValue(); + else + Opts.TokenCache = Opts.ImplicitPTHInclude; + Opts.UsePredefines = !Args.hasArg(OPT_undef); + Opts.DetailedRecord = Args.hasArg(OPT_detailed_preprocessing_record); + Opts.DisablePCHValidation = Args.hasArg(OPT_fno_validate_pch); + + Opts.DumpDeserializedPCHDecls = Args.hasArg(OPT_dump_deserialized_pch_decls); + for (arg_iterator it = Args.filtered_begin(OPT_error_on_deserialized_pch_decl), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.DeserializedPCHDeclsToErrorOn.insert(A->getValue()); + } + + if (const Arg *A = Args.getLastArg(OPT_preamble_bytes_EQ)) { + StringRef Value(A->getValue()); + size_t Comma = Value.find(','); + unsigned Bytes = 0; + unsigned EndOfLine = 0; + + if (Comma == StringRef::npos || + Value.substr(0, Comma).getAsInteger(10, Bytes) || + Value.substr(Comma + 1).getAsInteger(10, EndOfLine)) + Diags.Report(diag::err_drv_preamble_format); + else { + Opts.PrecompiledPreambleBytes.first = Bytes; + Opts.PrecompiledPreambleBytes.second = (EndOfLine != 0); + } + } + + // Add macros from the command line. + for (arg_iterator it = Args.filtered_begin(OPT_D, OPT_U), + ie = Args.filtered_end(); it != ie; ++it) { + if ((*it)->getOption().matches(OPT_D)) + Opts.addMacroDef((*it)->getValue()); + else + Opts.addMacroUndef((*it)->getValue()); + } + + Opts.MacroIncludes = Args.getAllArgValues(OPT_imacros); + + // Add the ordered list of -includes. + for (arg_iterator it = Args.filtered_begin(OPT_include), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.Includes.push_back(A->getValue()); + } + + for (arg_iterator it = Args.filtered_begin(OPT_chain_include), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.ChainedIncludes.push_back(A->getValue()); + } + + // Include 'altivec.h' if -faltivec option present + if (Args.hasArg(OPT_faltivec)) + Opts.Includes.push_back("altivec.h"); + + for (arg_iterator it = Args.filtered_begin(OPT_remap_file), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + std::pair<StringRef,StringRef> Split = + StringRef(A->getValue()).split(';'); + + if (Split.second.empty()) { + Diags.Report(diag::err_drv_invalid_remap_file) << A->getAsString(Args); + continue; + } + + Opts.addRemappedFile(Split.first, Split.second); + } + + if (Arg *A = Args.getLastArg(OPT_fobjc_arc_cxxlib_EQ)) { + StringRef Name = A->getValue(); + unsigned Library = llvm::StringSwitch<unsigned>(Name) + .Case("libc++", ARCXX_libcxx) + .Case("libstdc++", ARCXX_libstdcxx) + .Case("none", ARCXX_nolib) + .Default(~0U); + if (Library == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + else + Opts.ObjCXXARCStandardLibrary = (ObjCXXARCStandardLibraryKind)Library; + } +} + +static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, + ArgList &Args, + frontend::ActionKind Action) { + using namespace options; + + switch (Action) { + case frontend::ASTDeclList: + case frontend::ASTDump: + case frontend::ASTPrint: + case frontend::ASTView: + case frontend::EmitAssembly: + case frontend::EmitBC: + case frontend::EmitHTML: + case frontend::EmitLLVM: + case frontend::EmitLLVMOnly: + case frontend::EmitCodeGenOnly: + case frontend::EmitObj: + case frontend::FixIt: + case frontend::GenerateModule: + case frontend::GeneratePCH: + case frontend::GeneratePTH: + case frontend::ParseSyntaxOnly: + case frontend::ModuleFileInfo: + case frontend::VerifyPCH: + case frontend::PluginAction: + case frontend::PrintDeclContext: + case frontend::RewriteObjC: + case frontend::RewriteTest: + case frontend::RunAnalysis: + case frontend::MigrateSource: + Opts.ShowCPP = 0; + break; + + case frontend::DumpRawTokens: + case frontend::DumpTokens: + case frontend::InitOnly: + case frontend::PrintPreamble: + case frontend::PrintPreprocessedInput: + case frontend::RewriteMacros: + case frontend::RunPreprocessorOnly: + Opts.ShowCPP = !Args.hasArg(OPT_dM); + break; + } + + Opts.ShowComments = Args.hasArg(OPT_C); + Opts.ShowLineMarkers = !Args.hasArg(OPT_P); + Opts.ShowMacroComments = Args.hasArg(OPT_CC); + Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); +} + +static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) { + using namespace options; + Opts.ABI = Args.getLastArgValue(OPT_target_abi); + Opts.CPU = Args.getLastArgValue(OPT_target_cpu); + Opts.FPMath = Args.getLastArgValue(OPT_mfpmath); + Opts.FeaturesAsWritten = Args.getAllArgValues(OPT_target_feature); + Opts.LinkerVersion = Args.getLastArgValue(OPT_target_linker_version); + Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); +} + +bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, + const char *const *ArgBegin, + const char *const *ArgEnd, + DiagnosticsEngine &Diags) { + bool Success = true; + + // Parse the arguments. + std::unique_ptr<OptTable> Opts(createDriverOptTable()); + const unsigned IncludedFlagsBitmask = options::CC1Option; + unsigned MissingArgIndex, MissingArgCount; + std::unique_ptr<InputArgList> Args( + Opts->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount, + IncludedFlagsBitmask)); + + // Check for missing argument error. + if (MissingArgCount) { + Diags.Report(diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } + + // Issue errors on unknown arguments. + for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), + ie = Args->filtered_end(); it != ie; ++it) { + Diags.Report(diag::err_drv_unknown_argument) << (*it)->getAsString(*Args); + Success = false; + } + + Success = ParseAnalyzerArgs(*Res.getAnalyzerOpts(), *Args, Diags) && Success; + Success = ParseMigratorArgs(Res.getMigratorOpts(), *Args) && Success; + ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args); + Success = ParseDiagnosticArgs(Res.getDiagnosticOpts(), *Args, &Diags) + && Success; + ParseCommentArgs(Res.getLangOpts()->CommentOpts, *Args); + ParseFileSystemArgs(Res.getFileSystemOpts(), *Args); + // FIXME: We shouldn't have to pass the DashX option around here + InputKind DashX = ParseFrontendArgs(Res.getFrontendOpts(), *Args, Diags); + ParseTargetArgs(Res.getTargetOpts(), *Args); + Success = ParseCodeGenArgs(Res.getCodeGenOpts(), *Args, DashX, Diags, + Res.getTargetOpts()) && Success; + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), *Args); + if (DashX != IK_AST && DashX != IK_LLVM_IR) { + ParseLangArgs(*Res.getLangOpts(), *Args, DashX, Diags); + if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) + Res.getLangOpts()->ObjCExceptions = 1; + } + // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of + // PCH file and find the original header name. Remove the need to do that in + // ParsePreprocessorArgs and remove the FileManager + // parameters from the function and the "FileManager.h" #include. + FileManager FileMgr(Res.getFileSystemOpts()); + ParsePreprocessorArgs(Res.getPreprocessorOpts(), *Args, FileMgr, Diags); + ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), *Args, + Res.getFrontendOpts().ProgramAction); + return Success; +} + +namespace { + + class ModuleSignature { + SmallVector<uint64_t, 16> Data; + unsigned CurBit; + uint64_t CurValue; + + public: + ModuleSignature() : CurBit(0), CurValue(0) { } + + void add(uint64_t Value, unsigned Bits); + void add(StringRef Value); + void flush(); + + llvm::APInt getAsInteger() const; + }; +} + +void ModuleSignature::add(uint64_t Value, unsigned int NumBits) { + CurValue |= Value << CurBit; + if (CurBit + NumBits < 64) { + CurBit += NumBits; + return; + } + + // Add the current word. + Data.push_back(CurValue); + + if (CurBit) + CurValue = Value >> (64-CurBit); + else + CurValue = 0; + CurBit = (CurBit+NumBits) & 63; +} + +void ModuleSignature::flush() { + if (CurBit == 0) + return; + + Data.push_back(CurValue); + CurBit = 0; + CurValue = 0; +} + +void ModuleSignature::add(StringRef Value) { + for (StringRef::iterator I = Value.begin(), IEnd = Value.end(); I != IEnd;++I) + add(*I, 8); +} + +llvm::APInt ModuleSignature::getAsInteger() const { + return llvm::APInt(Data.size() * 64, Data); +} + +std::string CompilerInvocation::getModuleHash() const { + // Note: For QoI reasons, the things we use as a hash here should all be + // dumped via the -module-info flag. + using llvm::hash_code; + using llvm::hash_value; + using llvm::hash_combine; + + // Start the signature with the compiler version. + // FIXME: We'd rather use something more cryptographically sound than + // CityHash, but this will do for now. + hash_code code = hash_value(getClangFullRepositoryVersion()); + + // Extend the signature with the language options +#define LANGOPT(Name, Bits, Default, Description) \ + code = hash_combine(code, LangOpts->Name); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + code = hash_combine(code, static_cast<unsigned>(LangOpts->get##Name())); +#define BENIGN_LANGOPT(Name, Bits, Default, Description) +#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) +#include "clang/Basic/LangOptions.def" + + // Extend the signature with the target options. + code = hash_combine(code, TargetOpts->Triple, TargetOpts->CPU, + TargetOpts->ABI); + for (unsigned i = 0, n = TargetOpts->FeaturesAsWritten.size(); i != n; ++i) + code = hash_combine(code, TargetOpts->FeaturesAsWritten[i]); + + // Extend the signature with preprocessor options. + const PreprocessorOptions &ppOpts = getPreprocessorOpts(); + const HeaderSearchOptions &hsOpts = getHeaderSearchOpts(); + code = hash_combine(code, ppOpts.UsePredefines, ppOpts.DetailedRecord); + + for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator + I = getPreprocessorOpts().Macros.begin(), + IEnd = getPreprocessorOpts().Macros.end(); + I != IEnd; ++I) { + // If we're supposed to ignore this macro for the purposes of modules, + // don't put it into the hash. + if (!hsOpts.ModulesIgnoreMacros.empty()) { + // Check whether we're ignoring this macro. + StringRef MacroDef = I->first; + if (hsOpts.ModulesIgnoreMacros.count(MacroDef.split('=').first)) + continue; + } + + code = hash_combine(code, I->first, I->second); + } + + // Extend the signature with the sysroot. + code = hash_combine(code, hsOpts.Sysroot, hsOpts.UseBuiltinIncludes, + hsOpts.UseStandardSystemIncludes, + hsOpts.UseStandardCXXIncludes, + hsOpts.UseLibcxx); + code = hash_combine(code, hsOpts.ResourceDir); + + // Extend the signature with the user build path. + code = hash_combine(code, hsOpts.ModuleUserBuildPath); + + // Darwin-specific hack: if we have a sysroot, use the contents and + // modification time of + // $sysroot/System/Library/CoreServices/SystemVersion.plist + // as part of the module hash. + if (!hsOpts.Sysroot.empty()) { + SmallString<128> systemVersionFile; + systemVersionFile += hsOpts.Sysroot; + llvm::sys::path::append(systemVersionFile, "System"); + llvm::sys::path::append(systemVersionFile, "Library"); + llvm::sys::path::append(systemVersionFile, "CoreServices"); + llvm::sys::path::append(systemVersionFile, "SystemVersion.plist"); + + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer = + llvm::MemoryBuffer::getFile(systemVersionFile.str()); + if (buffer) { + code = hash_combine(code, buffer.get()->getBuffer()); + + struct stat statBuf; + if (stat(systemVersionFile.c_str(), &statBuf) == 0) + code = hash_combine(code, statBuf.st_mtime); + } + } + + return llvm::APInt(64, code).toString(36, /*Signed=*/false); +} + +namespace clang { + +template<typename IntTy> +static IntTy getLastArgIntValueImpl(const ArgList &Args, OptSpecifier Id, + IntTy Default, + DiagnosticsEngine *Diags) { + IntTy Res = Default; + if (Arg *A = Args.getLastArg(Id)) { + if (StringRef(A->getValue()).getAsInteger(10, Res)) { + if (Diags) + Diags->Report(diag::err_drv_invalid_int_value) << A->getAsString(Args) + << A->getValue(); + } + } + return Res; +} + + +// Declared in clang/Frontend/Utils.h. +int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<int>(Args, Id, Default, Diags); +} + +uint64_t getLastArgUInt64Value(const ArgList &Args, OptSpecifier Id, + uint64_t Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<uint64_t>(Args, Id, Default, Diags); +} + +void BuryPointer(const void *Ptr) { + // This function may be called only a small fixed amount of times per each + // invocation, otherwise we do actually have a leak which we want to report. + // If this function is called more than kGraveYardMaxSize times, the pointers + // will not be properly buried and a leak detector will report a leak, which + // is what we want in such case. + static const size_t kGraveYardMaxSize = 16; + LLVM_ATTRIBUTE_UNUSED static const void *GraveYard[kGraveYardMaxSize]; + static std::atomic<unsigned> GraveYardSize; + unsigned Idx = GraveYardSize++; + if (Idx >= kGraveYardMaxSize) + return; + GraveYard[Idx] = Ptr; +} + +IntrusiveRefCntPtr<vfs::FileSystem> +createVFSFromCompilerInvocation(const CompilerInvocation &CI, + DiagnosticsEngine &Diags) { + if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty()) + return vfs::getRealFileSystem(); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> + Overlay(new vfs::OverlayFileSystem(vfs::getRealFileSystem())); + // earlier vfs files are on the bottom + for (const std::string &File : CI.getHeaderSearchOpts().VFSOverlayFiles) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = + llvm::MemoryBuffer::getFile(File); + if (!Buffer) { + Diags.Report(diag::err_missing_vfs_overlay_file) << File; + return IntrusiveRefCntPtr<vfs::FileSystem>(); + } + + IntrusiveRefCntPtr<vfs::FileSystem> FS = + vfs::getVFSFromYAML(std::move(Buffer.get()), /*DiagHandler*/ nullptr); + if (!FS.get()) { + Diags.Report(diag::err_invalid_vfs_overlay) << File; + return IntrusiveRefCntPtr<vfs::FileSystem>(); + } + Overlay->pushOverlay(FS); + } + return Overlay; +} +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp new file mode 100644 index 0000000..4a8a8a0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -0,0 +1,89 @@ +//===--- CreateInvocationFromCommandLine.cpp - CompilerInvocation from Args ==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Construct a compiler invocation object for command line driver arguments +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" +using namespace clang; +using namespace llvm::opt; + +/// createInvocationFromCommandLine - Construct a compiler invocation object for +/// a command line argument vector. +/// +/// \return A CompilerInvocation, or 0 if none was built for the given +/// argument vector. +CompilerInvocation * +clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags) { + if (!Diags.get()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions); + } + + SmallVector<const char *, 16> Args; + Args.push_back("<clang>"); // FIXME: Remove dummy argument. + Args.insert(Args.end(), ArgList.begin(), ArgList.end()); + + // FIXME: Find a cleaner way to force the driver into restricted modes. + Args.push_back("-fsyntax-only"); + + // FIXME: We shouldn't have to pass in the path info. + driver::Driver TheDriver("clang", llvm::sys::getDefaultTargetTriple(), + *Diags); + + // Don't check that inputs exist, they may have been remapped. + TheDriver.setCheckInputsExist(false); + + std::unique_ptr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); + + // Just print the cc1 options if -### was present. + if (C->getArgs().hasArg(driver::options::OPT__HASH_HASH_HASH)) { + C->getJobs().Print(llvm::errs(), "\n", true); + return nullptr; + } + + // We expect to get back exactly one command job, if we didn't something + // failed. + const driver::JobList &Jobs = C->getJobs(); + if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) { + SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + Jobs.Print(OS, "; ", true); + Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); + return nullptr; + } + + const driver::Command &Cmd = cast<driver::Command>(*Jobs.begin()); + if (StringRef(Cmd.getCreator().getName()) != "clang") { + Diags->Report(diag::err_fe_expected_clang_command); + return nullptr; + } + + const ArgStringList &CCArgs = Cmd.getArguments(); + std::unique_ptr<CompilerInvocation> CI(new CompilerInvocation()); + if (!CompilerInvocation::CreateFromArgs(*CI, + const_cast<const char **>(CCArgs.data()), + const_cast<const char **>(CCArgs.data()) + + CCArgs.size(), + *Diags)) + return nullptr; + return CI.release(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp new file mode 100644 index 0000000..6ea8f51 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp @@ -0,0 +1,387 @@ +//===--- 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/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DependencyOutputOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/DirectoryLookup.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +struct DepCollectorPPCallbacks : public PPCallbacks { + DependencyCollector &DepCollector; + SourceManager &SM; + DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM) + : DepCollector(L), SM(SM) { } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override { + if (Reason != PPCallbacks::EnterFile) + return; + + // Dependency 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! + const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); + if (!FE) + return; + + StringRef Filename = FE->getName(); + + // Remove leading "./" (or ".//" or "././" etc.) + while (Filename.size() > 2 && Filename[0] == '.' && + llvm::sys::path::is_separator(Filename[1])) { + Filename = Filename.substr(1); + while (llvm::sys::path::is_separator(Filename[0])) + Filename = Filename.substr(1); + } + + DepCollector.maybeAddDependency(Filename, /*FromModule*/false, + FileType != SrcMgr::C_User, + /*IsModuleFile*/false, /*IsMissing*/false); + } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override { + if (!File) + DepCollector.maybeAddDependency(FileName, /*FromModule*/false, + /*IsSystem*/false, /*IsModuleFile*/false, + /*IsMissing*/true); + // Files that actually exist are handled by FileChanged. + } + + void EndOfMainFile() override { + DepCollector.finishedMainFile(); + } +}; + +struct DepCollectorASTListener : public ASTReaderListener { + DependencyCollector &DepCollector; + DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { } + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { + return DepCollector.needSystemDependencies(); + } + void visitModuleFile(StringRef Filename) override { + DepCollector.maybeAddDependency(Filename, /*FromModule*/true, + /*IsSystem*/false, /*IsModuleFile*/true, + /*IsMissing*/false); + } + bool visitInputFile(StringRef Filename, bool IsSystem, + bool IsOverridden) override { + if (IsOverridden) + return true; + + DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem, + /*IsModuleFile*/false, /*IsMissing*/false); + return true; + } +}; +} // end anonymous namespace + +void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing) { + if (Seen.insert(Filename).second && + sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing)) + Dependencies.push_back(Filename); +} + +static bool isSpecialFilename(StringRef Filename) { + return llvm::StringSwitch<bool>(Filename) + .Case("<built-in>", true) + .Case("<stdin>", true) + .Default(false); +} + +bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing) { + return !isSpecialFilename(Filename) && + (needSystemDependencies() || !IsSystem); +} + +DependencyCollector::~DependencyCollector() { } +void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { + PP.addPPCallbacks( + llvm::make_unique<DepCollectorPPCallbacks>(*this, PP.getSourceManager())); +} +void DependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(llvm::make_unique<DepCollectorASTListener>(*this)); +} + +namespace { +/// Private implementation for DependencyFileGenerator +class DFGImpl : public PPCallbacks { + std::vector<std::string> Files; + llvm::StringSet<> FilesSet; + const Preprocessor *PP; + std::string OutputFile; + std::vector<std::string> Targets; + bool IncludeSystemHeaders; + bool PhonyTarget; + bool AddMissingHeaderDeps; + bool SeenMissingHeader; + bool IncludeModuleFiles; +private: + bool FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType); + void OutputDependencyFile(); + +public: + DFGImpl(const Preprocessor *_PP, const DependencyOutputOptions &Opts) + : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets), + IncludeSystemHeaders(Opts.IncludeSystemHeaders), + PhonyTarget(Opts.UsePhonyTargets), + AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), + SeenMissingHeader(false), + IncludeModuleFiles(Opts.IncludeModuleFiles) {} + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override; + + void EndOfMainFile() override { + OutputDependencyFile(); + } + + void AddFilename(StringRef Filename); + bool includeSystemHeaders() const { return IncludeSystemHeaders; } + bool includeModuleFiles() const { return IncludeModuleFiles; } +}; + +class DFGASTReaderListener : public ASTReaderListener { + DFGImpl &Parent; +public: + DFGASTReaderListener(DFGImpl &Parent) + : Parent(Parent) { } + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { + return Parent.includeSystemHeaders(); + } + void visitModuleFile(StringRef Filename) override; + bool visitInputFile(StringRef Filename, bool isSystem, + bool isOverridden) override; +}; +} + +DependencyFileGenerator::DependencyFileGenerator(void *Impl) +: Impl(Impl) { } + +DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor( + clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) { + + if (Opts.Targets.empty()) { + PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); + return nullptr; + } + + // Disable the "file not found" diagnostic if the -MG option was given. + if (Opts.AddMissingHeaderDeps) + PP.SetSuppressIncludeNotFoundError(true); + + DFGImpl *Callback = new DFGImpl(&PP, Opts); + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callback)); + return new DependencyFileGenerator(Callback); +} + +void DependencyFileGenerator::AttachToASTReader(ASTReader &R) { + DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl); + assert(I && "missing implementation"); + R.addListener(llvm::make_unique<DFGASTReaderListener>(*I)); +} + +/// FileMatchesDepCriteria - Determine whether the given Filename should be +/// considered as a dependency. +bool DFGImpl::FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType) { + if (isSpecialFilename(Filename)) + return false; + + if (IncludeSystemHeaders) + return true; + + return FileType == SrcMgr::C_User; +} + +void DFGImpl::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + if (Reason != PPCallbacks::EnterFile) + return; + + // Dependency 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.getExpansionLoc(Loc))); + if (!FE) return; + + StringRef Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) + return; + + // Remove leading "./" (or ".//" or "././" etc.) + while (Filename.size() > 2 && Filename[0] == '.' && + llvm::sys::path::is_separator(Filename[1])) { + Filename = Filename.substr(1); + while (llvm::sys::path::is_separator(Filename[0])) + Filename = Filename.substr(1); + } + + AddFilename(Filename); +} + +void DFGImpl::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + if (!File) { + if (AddMissingHeaderDeps) + AddFilename(FileName); + else + SeenMissingHeader = true; + } +} + +void DFGImpl::AddFilename(StringRef Filename) { + if (FilesSet.insert(Filename).second) + Files.push_back(Filename); +} + +/// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or +/// other scary characters. +static void PrintFilename(raw_ostream &OS, StringRef Filename) { + for (unsigned i = 0, e = Filename.size(); i != e; ++i) { + if (Filename[i] == ' ' || Filename[i] == '#') + OS << '\\'; + else if (Filename[i] == '$') // $ is escaped by $$. + OS << '$'; + OS << Filename[i]; + } +} + +void DFGImpl::OutputDependencyFile() { + if (SeenMissingHeader) { + llvm::sys::fs::remove(OutputFile); + return; + } + + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile + << EC.message(); + return; + } + + // 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; + } else if (Columns + N + 2 > MaxColumns) { + Columns = N + 2; + OS << " \\\n "; + } else { + Columns += N + 1; + OS << ' '; + } + // Targets already quoted as needed. + 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 << ' '; + PrintFilename(OS, *I); + Columns += N + 1; + } + OS << '\n'; + + // Create phony targets if requested. + if (PhonyTarget && !Files.empty()) { + // 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'; + PrintFilename(OS, *I); + OS << ":\n"; + } + } +} + +bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename, + bool IsSystem, bool IsOverridden) { + assert(!IsSystem || needsSystemInputFileVisitation()); + if (IsOverridden) + return true; + + Parent.AddFilename(Filename); + return true; +} + +void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename) { + if (Parent.includeModuleFiles()) + Parent.AddFilename(Filename); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp new file mode 100644 index 0000000..67a977e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp @@ -0,0 +1,138 @@ +//===--- DependencyGraph.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 a header dependency graph in DOT format, for use +// with, e.g., GraphViz. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +namespace DOT = llvm::DOT; + +namespace { +class DependencyGraphCallback : public PPCallbacks { + const Preprocessor *PP; + std::string OutputFile; + std::string SysRoot; + llvm::SetVector<const FileEntry *> AllFiles; + typedef llvm::DenseMap<const FileEntry *, + SmallVector<const FileEntry *, 2> > DependencyMap; + + DependencyMap Dependencies; + +private: + raw_ostream &writeNodeReference(raw_ostream &OS, + const FileEntry *Node); + void OutputGraphFile(); + +public: + DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, + StringRef SysRoot) + : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override; + + void EndOfMainFile() override { + OutputGraphFile(); + } + +}; +} + +void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot) { + PP.addPPCallbacks(llvm::make_unique<DependencyGraphCallback>(&PP, OutputFile, + SysRoot)); +} + +void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + const FileEntry *FromFile + = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (!FromFile) + return; + + Dependencies[FromFile].push_back(File); + + AllFiles.insert(File); + AllFiles.insert(FromFile); +} + +raw_ostream & +DependencyGraphCallback::writeNodeReference(raw_ostream &OS, + const FileEntry *Node) { + OS << "header_" << Node->getUID(); + return OS; +} + +void DependencyGraphCallback::OutputGraphFile() { + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile + << EC.message(); + return; + } + + OS << "digraph \"dependencies\" {\n"; + + // Write the nodes + for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { + // Write the node itself. + OS.indent(2); + writeNodeReference(OS, AllFiles[I]); + OS << " [ shape=\"box\", label=\""; + StringRef FileName = AllFiles[I]->getName(); + if (FileName.startswith(SysRoot)) + FileName = FileName.substr(SysRoot.size()); + + OS << DOT::EscapeString(FileName) + << "\"];\n"; + } + + // Write the edges + for (DependencyMap::iterator F = Dependencies.begin(), + FEnd = Dependencies.end(); + F != FEnd; ++F) { + for (unsigned I = 0, N = F->second.size(); I != N; ++I) { + OS.indent(2); + writeNodeReference(OS, F->first); + OS << " -> "; + writeNodeReference(OS, F->second[I]); + OS << ";\n"; + } + } + OS << "}\n"; +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp new file mode 100644 index 0000000..c63e98d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,514 @@ +//===--- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +using namespace clang; + +/// \brief Retrieve the name of the immediate macro expansion. +/// +/// This routine starts from a source location, and finds the name of the macro +/// responsible for its immediate expansion. It looks through any intervening +/// macro argument expansions to compute this. It returns a StringRef which +/// refers to the SourceManager-owned buffer of the source where that macro +/// name is spelled. Thus, the result shouldn't out-live that SourceManager. +/// +/// This differs from Lexer::getImmediateMacroName in that any macro argument +/// location will result in the topmost function macro that accepted it. +/// e.g. +/// \code +/// MAC1( MAC2(foo) ) +/// \endcode +/// for location of 'foo' token, this function will return "MAC1" while +/// Lexer::getImmediateMacroName will return "MAC2". +static StringRef getImmediateMacroName(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(Loc.isMacroID() && "Only reasonble to call this on macros"); + // Walk past macro argument expanions. + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + + // If the macro's spelling has no FileID, then it's actually a token paste + // or stringization (or similar) and not a macro at all. + if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(Loc)))) + return StringRef(); + + // Find the spelling location of the start of the non-argument expansion + // range. This is where the macro name was spelled in order to begin + // expanding this macro. + Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); + + // Dig out the buffer where the macro name was spelled and the extents of the + // name so that we can render it into the expansion note. + std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc); + unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); + StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); + return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); +} + +DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} + +DiagnosticRenderer::~DiagnosticRenderer() {} + +namespace { + +class FixitReceiver : public edit::EditsReceiver { + SmallVectorImpl<FixItHint> &MergedFixits; + +public: + FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) + : MergedFixits(MergedFixits) { } + void insert(SourceLocation loc, StringRef text) override { + MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); + } + void replace(CharSourceRange range, StringRef text) override { + MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); + } +}; + +} + +static void mergeFixits(ArrayRef<FixItHint> FixItHints, + const SourceManager &SM, const LangOptions &LangOpts, + SmallVectorImpl<FixItHint> &MergedFixits) { + edit::Commit commit(SM, LangOpts); + for (ArrayRef<FixItHint>::const_iterator + I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) { + const FixItHint &Hint = *I; + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + + edit::EditedSource Editor(SM, LangOpts); + if (Editor.commit(commit)) { + FixitReceiver Rec(MergedFixits); + Editor.applyRewrites(Rec); + } +} + +void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> FixItHints, + const SourceManager *SM, + DiagOrStoredDiag D) { + assert(SM || Loc.isInvalid()); + + beginDiagnostic(D, Level); + + if (!Loc.isValid()) + // If we have no source location, just emit the diagnostic message. + emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, SM, D); + else { + // Get the ranges into a local array we can hack on. + SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), + Ranges.end()); + + SmallVector<FixItHint, 8> MergedFixits; + if (!FixItHints.empty()) { + mergeFixits(FixItHints, *SM, LangOpts, MergedFixits); + FixItHints = MergedFixits; + } + + for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(), + E = FixItHints.end(); + I != E; ++I) + if (I->RemoveRange.isValid()) + MutableRanges.push_back(I->RemoveRange); + + SourceLocation UnexpandedLoc = Loc; + + // Find the ultimate expansion location for the diagnostic. + Loc = SM->getFileLoc(Loc); + + PresumedLoc PLoc = SM->getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + emitIncludeStack(Loc, PLoc, Level, *SM); + + // Next, emit the actual diagnostic message and caret. + emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D); + emitCaret(Loc, Level, MutableRanges, FixItHints, *SM); + + // If this location is within a macro, walk from UnexpandedLoc up to Loc + // and produce a macro backtrace. + if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { + unsigned MacroDepth = 0; + emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints, *SM, + MacroDepth); + } + } + + LastLoc = Loc; + LastLevel = Level; + + endDiagnostic(D, Level); +} + + +void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { + emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), + Diag.getRanges(), Diag.getFixIts(), + Diag.getLocation().isValid() ? &Diag.getLocation().getManager() + : nullptr, + &Diag); +} + +void DiagnosticRenderer::emitBasicNote(StringRef Message) { + emitDiagnosticMessage( + SourceLocation(), PresumedLoc(), DiagnosticsEngine::Note, Message, + None, nullptr, DiagOrStoredDiag()); +} + +/// \brief Prints an include stack when appropriate for a particular +/// diagnostic level and location. +/// +/// This routine handles all the logic of suppressing particular include +/// stacks (such as those for notes) and duplicate include stacks when +/// repeated warnings occur within the same file. It also handles the logic +/// of customizing the formatting and display of the include stack. +/// +/// \param Loc The diagnostic location. +/// \param PLoc The presumed location of the diagnostic location. +/// \param Level The diagnostic level of the message this stack pertains to. +void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + const SourceManager &SM) { + SourceLocation IncludeLoc = PLoc.getIncludeLoc(); + + // Skip redundant include stacks altogether. + if (LastIncludeLoc == IncludeLoc) + return; + + LastIncludeLoc = IncludeLoc; + + if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) + return; + + if (IncludeLoc.isValid()) + emitIncludeStackRecursively(IncludeLoc, SM); + else { + emitModuleBuildStack(SM); + emitImportStack(Loc, SM); + } +} + +/// \brief Helper to recursivly walk up the include stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isInvalid()) { + emitModuleBuildStack(SM); + return; + } + + PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + if (PLoc.isInvalid()) + return; + + // If this source location was imported from a module, print the module + // import stack rather than the + // FIXME: We want submodule granularity here. + std::pair<SourceLocation, StringRef> Imported = SM.getModuleImportLoc(Loc); + if (Imported.first.isValid()) { + // This location was imported by a module. Emit the module import stack. + emitImportStackRecursively(Imported.first, Imported.second, SM); + return; + } + + // Emit the other include frames first. + emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM); + + // Emit the inclusion text/note. + emitIncludeLocation(Loc, PLoc, SM); +} + +/// \brief Emit the module import stack associated with the current location. +void DiagnosticRenderer::emitImportStack(SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isInvalid()) { + emitModuleBuildStack(SM); + return; + } + + std::pair<SourceLocation, StringRef> NextImportLoc + = SM.getModuleImportLoc(Loc); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); +} + +/// \brief Helper to recursivly walk up the import stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitImportStackRecursively(SourceLocation Loc, + StringRef ModuleName, + const SourceManager &SM) { + if (Loc.isInvalid()) { + return; + } + + PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + if (PLoc.isInvalid()) + return; + + // Emit the other import frames first. + std::pair<SourceLocation, StringRef> NextImportLoc + = SM.getModuleImportLoc(Loc); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); + + // Emit the inclusion text/note. + emitImportLocation(Loc, PLoc, ModuleName, SM); +} + +/// \brief Emit the module build stack, for cases where a module is (re-)built +/// on demand. +void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { + ModuleBuildStack Stack = SM.getModuleBuildStack(); + for (unsigned I = 0, N = Stack.size(); I != N; ++I) { + const SourceManager &CurSM = Stack[I].second.getManager(); + SourceLocation CurLoc = Stack[I].second; + emitBuildingModuleLocation(CurLoc, + CurSM.getPresumedLoc(CurLoc, + DiagOpts->ShowPresumedLoc), + Stack[I].first, + CurSM); + } +} + +// Helper function to fix up source ranges. It takes in an array of ranges, +// and outputs an array of ranges where we want to draw the range highlighting +// around the location specified by CaretLoc. +// +// To find locations which correspond to the caret, we crawl the macro caller +// chain for the beginning and end of each range. If the caret location +// is in a macro expansion, we search each chain for a location +// in the same expansion as the caret; otherwise, we crawl to the top of +// each chain. Two locations are part of the same macro expansion +// iff the FileID is the same. +static void mapDiagnosticRanges( + SourceLocation CaretLoc, + ArrayRef<CharSourceRange> Ranges, + SmallVectorImpl<CharSourceRange> &SpellingRanges, + const SourceManager *SM) { + FileID CaretLocFileID = SM->getFileID(CaretLoc); + + for (ArrayRef<CharSourceRange>::const_iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) { + SourceLocation Begin = I->getBegin(), End = I->getEnd(); + bool IsTokenRange = I->isTokenRange(); + + FileID BeginFileID = SM->getFileID(Begin); + FileID EndFileID = SM->getFileID(End); + + // Find the common parent for the beginning and end of the range. + + // First, crawl the expansion chain for the beginning of the range. + llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap; + while (Begin.isMacroID() && BeginFileID != EndFileID) { + BeginLocsMap[BeginFileID] = Begin; + Begin = SM->getImmediateExpansionRange(Begin).first; + BeginFileID = SM->getFileID(Begin); + } + + // Then, crawl the expansion chain for the end of the range. + if (BeginFileID != EndFileID) { + while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) { + End = SM->getImmediateExpansionRange(End).second; + EndFileID = SM->getFileID(End); + } + if (End.isMacroID()) { + Begin = BeginLocsMap[EndFileID]; + BeginFileID = EndFileID; + } + } + + while (Begin.isMacroID() && BeginFileID != CaretLocFileID) { + if (SM->isMacroArgExpansion(Begin)) { + Begin = SM->getImmediateSpellingLoc(Begin); + End = SM->getImmediateSpellingLoc(End); + } else { + Begin = SM->getImmediateExpansionRange(Begin).first; + End = SM->getImmediateExpansionRange(End).second; + } + BeginFileID = SM->getFileID(Begin); + if (BeginFileID != SM->getFileID(End)) { + // FIXME: Ugly hack to stop a crash; this code is making bad + // assumptions and it's too complicated for me to reason + // about. + Begin = End = SourceLocation(); + break; + } + } + + // Return the spelling location of the beginning and end of the range. + Begin = SM->getSpellingLoc(Begin); + End = SM->getSpellingLoc(End); + SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End), + IsTokenRange)); + } +} + +void DiagnosticRenderer::emitCaret(SourceLocation Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + emitCodeContext(Loc, Level, SpellingRanges, Hints, SM); +} + +/// \brief Recursively emit notes for each macro expansion and caret +/// diagnostics where appropriate. +/// +/// Walks up the macro expansion stack printing expansion notes, the code +/// snippet, caret, underlines and FixItHint display as appropriate at each +/// level. +/// +/// \param Loc The location for this caret. +/// \param Level The diagnostic level currently being emitted. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +/// \param OnMacroInst The current depth of the macro expansion stack. +void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM, + unsigned &MacroDepth, + unsigned OnMacroInst) { + assert(!Loc.isInvalid() && "must have a valid source location here"); + + // Walk up to the caller of this macro, and produce a backtrace down to there. + SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc); + if (OneLevelUp.isMacroID()) + emitMacroExpansions(OneLevelUp, Level, Ranges, Hints, SM, + MacroDepth, OnMacroInst + 1); + else + MacroDepth = OnMacroInst + 1; + + unsigned MacroSkipStart = 0, MacroSkipEnd = 0; + if (MacroDepth > DiagOpts->MacroBacktraceLimit && + DiagOpts->MacroBacktraceLimit != 0) { + MacroSkipStart = DiagOpts->MacroBacktraceLimit / 2 + + DiagOpts->MacroBacktraceLimit % 2; + MacroSkipEnd = MacroDepth - DiagOpts->MacroBacktraceLimit / 2; + } + + // Whether to suppress printing this macro expansion. + bool Suppressed = (OnMacroInst >= MacroSkipStart && + OnMacroInst < MacroSkipEnd); + + if (Suppressed) { + // Tell the user that we've skipped contexts. + if (OnMacroInst == MacroSkipStart) { + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " + "see all)"; + emitBasicNote(Message.str()); + } + return; + } + + // Find the spelling location for the macro definition. We must use the + // spelling location here to avoid emitting a macro bactrace for the note. + SourceLocation SpellingLoc = Loc; + // If this is the expansion of a macro argument, point the caret at the + // use of the argument in the definition of the macro, not the expansion. + if (SM.isMacroArgExpansion(Loc)) + SpellingLoc = SM.getImmediateExpansionRange(Loc).first; + SpellingLoc = SM.getSpellingLoc(SpellingLoc); + + // Map the ranges into the FileID of the diagnostic location. + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + + SmallString<100> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + StringRef MacroName = getImmediateMacroName(Loc, SM, LangOpts); + if (MacroName.empty()) + Message << "expanded from here"; + else + Message << "expanded from macro '" << MacroName << "'"; + emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), + SpellingRanges, None, &SM); +} + +DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} + +void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":"; + emitNote(Loc, Message.str(), &SM); +} + +void DiagnosticNoteRenderer::emitImportLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; + emitNote(Loc, Message.str(), &SM); +} + +void +DiagnosticNoteRenderer::emitBuildingModuleLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + if (PLoc.getFilename()) + Message << "while building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; + else + Message << "while building module '" << ModuleName << "':"; + emitNote(Loc, Message.str(), &SM); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp new file mode 100644 index 0000000..c81c81a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp @@ -0,0 +1,580 @@ +//===--- FrontendAction.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendAction.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/GlobalModuleIndex.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <system_error> +using namespace clang; + +template class llvm::Registry<clang::PluginASTAction>; + +namespace { + +class DelegatingDeserializationListener : public ASTDeserializationListener { + ASTDeserializationListener *Previous; + bool DeletePrevious; + +public: + explicit DelegatingDeserializationListener( + ASTDeserializationListener *Previous, bool DeletePrevious) + : Previous(Previous), DeletePrevious(DeletePrevious) {} + virtual ~DelegatingDeserializationListener() { + if (DeletePrevious) + delete Previous; + } + + void ReaderInitialized(ASTReader *Reader) override { + if (Previous) + Previous->ReaderInitialized(Reader); + } + void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II) override { + if (Previous) + Previous->IdentifierRead(ID, II); + } + void TypeRead(serialization::TypeIdx Idx, QualType T) override { + if (Previous) + Previous->TypeRead(Idx, T); + } + void DeclRead(serialization::DeclID ID, const Decl *D) override { + if (Previous) + Previous->DeclRead(ID, D); + } + void SelectorRead(serialization::SelectorID ID, Selector Sel) override { + if (Previous) + Previous->SelectorRead(ID, Sel); + } + void MacroDefinitionRead(serialization::PreprocessedEntityID PPID, + MacroDefinition *MD) override { + if (Previous) + Previous->MacroDefinitionRead(PPID, MD); + } +}; + +/// \brief Dumps deserialized declarations. +class DeserializedDeclsDumper : public DelegatingDeserializationListener { +public: + explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous, + bool DeletePrevious) + : DelegatingDeserializationListener(Previous, DeletePrevious) {} + + void DeclRead(serialization::DeclID ID, const Decl *D) override { + llvm::outs() << "PCH DECL: " << D->getDeclKindName(); + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + llvm::outs() << " - " << *ND; + llvm::outs() << "\n"; + + DelegatingDeserializationListener::DeclRead(ID, D); + } +}; + +/// \brief Checks deserialized declarations and emits error if a name +/// matches one given in command-line using -error-on-deserialized-decl. +class DeserializedDeclsChecker : public DelegatingDeserializationListener { + ASTContext &Ctx; + std::set<std::string> NamesToCheck; + +public: + DeserializedDeclsChecker(ASTContext &Ctx, + const std::set<std::string> &NamesToCheck, + ASTDeserializationListener *Previous, + bool DeletePrevious) + : DelegatingDeserializationListener(Previous, DeletePrevious), Ctx(Ctx), + NamesToCheck(NamesToCheck) {} + + void DeclRead(serialization::DeclID ID, const Decl *D) override { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) { + unsigned DiagID + = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, + "%0 was deserialized"); + Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID) + << ND->getNameAsString(); + } + + DelegatingDeserializationListener::DeclRead(ID, D); + } +}; + +} // end anonymous namespace + +FrontendAction::FrontendAction() : Instance(nullptr) {} + +FrontendAction::~FrontendAction() {} + +void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, + std::unique_ptr<ASTUnit> AST) { + this->CurrentInput = CurrentInput; + CurrentASTUnit = std::move(AST); +} + +std::unique_ptr<ASTConsumer> +FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::unique_ptr<ASTConsumer> Consumer = CreateASTConsumer(CI, InFile); + if (!Consumer) + return nullptr; + + if (CI.getFrontendOpts().AddPluginActions.size() == 0) + return Consumer; + + // Make sure the non-plugin consumer is first, so that plugins can't + // modifiy the AST. + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(std::move(Consumer)); + + for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size(); + i != e; ++i) { + // This is O(|plugins| * |add_plugins|), but since both numbers are + // way below 50 in practice, that's ok. + for (FrontendPluginRegistry::iterator + it = FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() != CI.getFrontendOpts().AddPluginActions[i]) + continue; + std::unique_ptr<PluginASTAction> P = it->instantiate(); + if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i])) + Consumers.push_back(P->CreateASTConsumer(CI, InFile)); + } + } + + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +bool FrontendAction::BeginSourceFile(CompilerInstance &CI, + const FrontendInputFile &Input) { + assert(!Instance && "Already processing a source file!"); + assert(!Input.isEmpty() && "Unexpected empty filename!"); + setCurrentInput(Input); + setCompilerInstance(&CI); + + StringRef InputFile = Input.getFile(); + bool HasBegunSourceFile = false; + if (!BeginInvocation(CI)) + goto failure; + + // AST files follow a very different path, since they share objects via the + // AST unit. + if (Input.getKind() == IK_AST) { + assert(!usesPreprocessorOnly() && + "Attempt to pass AST file to preprocessor only action!"); + assert(hasASTFileSupport() && + "This action does not have AST file support!"); + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); + + std::unique_ptr<ASTUnit> AST = + ASTUnit::LoadFromASTFile(InputFile, Diags, CI.getFileSystemOpts()); + + if (!AST) + goto failure; + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr); + HasBegunSourceFile = true; + + // Set the shared objects, these are reset when we finish processing the + // file, otherwise the CompilerInstance will happily destroy them. + CI.setFileManager(&AST->getFileManager()); + CI.setSourceManager(&AST->getSourceManager()); + CI.setPreprocessor(&AST->getPreprocessor()); + CI.setASTContext(&AST->getASTContext()); + + setCurrentInput(Input, std::move(AST)); + + // Initialize the action. + if (!BeginSourceFileAction(CI, InputFile)) + goto failure; + + // Create the AST consumer. + CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile)); + if (!CI.hasASTConsumer()) + goto failure; + + return true; + } + + if (!CI.hasVirtualFileSystem()) { + if (IntrusiveRefCntPtr<vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(CI.getInvocation(), + CI.getDiagnostics())) + CI.setVirtualFileSystem(VFS); + else + goto failure; + } + + // Set up the file and source managers, if needed. + if (!CI.hasFileManager()) + CI.createFileManager(); + if (!CI.hasSourceManager()) + CI.createSourceManager(CI.getFileManager()); + + // IR files bypass the rest of initialization. + if (Input.getKind() == IK_LLVM_IR) { + assert(hasIRSupport() && + "This action does not have IR file support!"); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr); + HasBegunSourceFile = true; + + // Initialize the action. + if (!BeginSourceFileAction(CI, InputFile)) + goto failure; + + // Initialize the main file entry. + if (!CI.InitializeSourceManager(CurrentInput)) + goto failure; + + return true; + } + + // If the implicit PCH include is actually a directory, rather than + // a single file, search for a suitable PCH file in that directory. + if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + FileManager &FileMgr = CI.getFileManager(); + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + StringRef PCHInclude = PPOpts.ImplicitPCHInclude; + if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { + std::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(PCHDir->getName(), DirNative); + bool Found = false; + for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this is an acceptable AST file. + if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr, + CI.getLangOpts(), + CI.getTargetOpts(), + CI.getPreprocessorOpts())) { + PPOpts.ImplicitPCHInclude = Dir->path(); + Found = true; + break; + } + } + + if (!Found) { + CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; + return true; + } + } + } + + // Set up the preprocessor if needed. When parsing model files the + // preprocessor of the original source is reused. + if (!isModelParsingAction()) + CI.createPreprocessor(getTranslationUnitKind()); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), + &CI.getPreprocessor()); + HasBegunSourceFile = true; + + // Initialize the action. + if (!BeginSourceFileAction(CI, InputFile)) + goto failure; + + // Initialize the main file entry. It is important that this occurs after + // BeginSourceFileAction, which may change CurrentInput during module builds. + if (!CI.InitializeSourceManager(CurrentInput)) + goto failure; + + // Create the AST context and consumer unless this is a preprocessor only + // action. + if (!usesPreprocessorOnly()) { + // Parsing a model file should reuse the existing ASTContext. + if (!isModelParsingAction()) + CI.createASTContext(); + + std::unique_ptr<ASTConsumer> Consumer = + CreateWrappedASTConsumer(CI, InputFile); + if (!Consumer) + goto failure; + + // FIXME: should not overwrite ASTMutationListener when parsing model files? + if (!isModelParsingAction()) + CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); + + if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { + // Convert headers to PCH and chain them. + IntrusiveRefCntPtr<ExternalSemaSource> source, FinalReader; + source = createChainedIncludesSource(CI, FinalReader); + if (!source) + goto failure; + CI.setModuleManager(static_cast<ASTReader *>(FinalReader.get())); + CI.getASTContext().setExternalSource(source); + } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + // Use PCH. + assert(hasPCHSupport() && "This action does not have PCH support!"); + ASTDeserializationListener *DeserialListener = + Consumer->GetASTDeserializationListener(); + bool DeleteDeserialListener = false; + if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) { + DeserialListener = new DeserializedDeclsDumper(DeserialListener, + DeleteDeserialListener); + DeleteDeserialListener = true; + } + if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) { + DeserialListener = new DeserializedDeclsChecker( + CI.getASTContext(), + CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn, + DeserialListener, DeleteDeserialListener); + DeleteDeserialListener = true; + } + CI.createPCHExternalASTSource( + CI.getPreprocessorOpts().ImplicitPCHInclude, + CI.getPreprocessorOpts().DisablePCHValidation, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener, + DeleteDeserialListener); + if (!CI.getASTContext().getExternalSource()) + goto failure; + } + + CI.setASTConsumer(std::move(Consumer)); + if (!CI.hasASTConsumer()) + goto failure; + } + + // Initialize built-in info as long as we aren't using an external AST + // source. + if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) { + Preprocessor &PP = CI.getPreprocessor(); + + // If modules are enabled, create the module manager before creating + // any builtins, so that all declarations know that they might be + // extended by an external source. + if (CI.getLangOpts().Modules) + CI.createModuleManager(); + + PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + // FIXME: If this is a problem, recover from it by creating a multiplex + // source. + assert((!CI.getLangOpts().Modules || CI.getModuleManager()) && + "modules enabled but created an external source that " + "doesn't support modules"); + } + + // If we were asked to load any module files, do so now. + for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) + if (!CI.loadModuleFile(ModuleFile)) + goto failure; + + // If there is a layout overrides file, attach an external AST source that + // provides the layouts from that file. + if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && + CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { + IntrusiveRefCntPtr<ExternalASTSource> + Override(new LayoutOverrideSource( + CI.getFrontendOpts().OverrideRecordLayoutsFile)); + CI.getASTContext().setExternalSource(Override); + } + + return true; + + // If we failed, reset state since the client will not end up calling the + // matching EndSourceFile(). + failure: + if (isCurrentFileAST()) { + CI.setASTContext(nullptr); + CI.setPreprocessor(nullptr); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + } + + if (HasBegunSourceFile) + CI.getDiagnosticClient().EndSourceFile(); + CI.clearOutputFiles(/*EraseFiles=*/true); + setCurrentInput(FrontendInputFile()); + setCompilerInstance(nullptr); + return false; +} + +bool FrontendAction::Execute() { + CompilerInstance &CI = getCompilerInstance(); + + if (CI.hasFrontendTimer()) { + llvm::TimeRegion Timer(CI.getFrontendTimer()); + ExecuteAction(); + } + else ExecuteAction(); + + // If we are supposed to rebuild the global module index, do so now unless + // there were any module-build failures. + if (CI.shouldBuildGlobalModuleIndex() && CI.hasFileManager() && + CI.hasPreprocessor()) { + GlobalModuleIndex::writeIndex( + CI.getFileManager(), + CI.getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + } + + return true; +} + +void FrontendAction::EndSourceFile() { + CompilerInstance &CI = getCompilerInstance(); + + // Inform the diagnostic client we are done with this source file. + CI.getDiagnosticClient().EndSourceFile(); + + // Inform the preprocessor we are done. + if (CI.hasPreprocessor()) + CI.getPreprocessor().EndSourceFile(); + + // Finalize the action. + EndSourceFileAction(); + + // Sema references the ast consumer, so reset sema first. + // + // FIXME: There is more per-file stuff we could just drop here? + bool DisableFree = CI.getFrontendOpts().DisableFree; + if (DisableFree) { + if (!isCurrentFileAST()) { + CI.resetAndLeakSema(); + CI.resetAndLeakASTContext(); + } + BuryPointer(CI.takeASTConsumer().get()); + } else { + if (!isCurrentFileAST()) { + CI.setSema(nullptr); + CI.setASTContext(nullptr); + } + CI.setASTConsumer(nullptr); + } + + if (CI.getFrontendOpts().ShowStats) { + llvm::errs() << "\nSTATISTICS FOR '" << getCurrentFile() << "':\n"; + CI.getPreprocessor().PrintStats(); + CI.getPreprocessor().getIdentifierTable().PrintStats(); + CI.getPreprocessor().getHeaderSearchInfo().PrintStats(); + CI.getSourceManager().PrintStats(); + llvm::errs() << "\n"; + } + + // Cleanup the output streams, and erase the output files if instructed by the + // FrontendAction. + CI.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles()); + + // FIXME: Only do this if DisableFree is set. + if (isCurrentFileAST()) { + CI.resetAndLeakSema(); + CI.resetAndLeakASTContext(); + CI.resetAndLeakPreprocessor(); + CI.resetAndLeakSourceManager(); + CI.resetAndLeakFileManager(); + } + + setCompilerInstance(nullptr); + setCurrentInput(FrontendInputFile()); +} + +bool FrontendAction::shouldEraseOutputFiles() { + return getCompilerInstance().getDiagnostics().hasErrorOccurred(); +} + +//===----------------------------------------------------------------------===// +// Utility Actions +//===----------------------------------------------------------------------===// + +void ASTFrontendAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + if (!CI.hasPreprocessor()) + return; + + // FIXME: Move the truncation aspect of this into Sema, we delayed this till + // here so the source manager would be initialized. + if (hasCodeCompletionSupport() && + !CI.getFrontendOpts().CodeCompletionAt.FileName.empty()) + CI.createCodeCompletionConsumer(); + + // Use a code completion consumer? + CodeCompleteConsumer *CompletionConsumer = nullptr; + if (CI.hasCodeCompletionConsumer()) + CompletionConsumer = &CI.getCodeCompletionConsumer(); + + if (!CI.hasSema()) + CI.createSema(getTranslationUnitKind(), CompletionConsumer); + + ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, + CI.getFrontendOpts().SkipFunctionBodies); +} + +void PluginASTAction::anchor() { } + +std::unique_ptr<ASTConsumer> +PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); +} + +std::unique_ptr<ASTConsumer> +WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return WrappedAction->CreateASTConsumer(CI, InFile); +} +bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { + return WrappedAction->BeginInvocation(CI); +} +bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + WrappedAction->setCurrentInput(getCurrentInput()); + WrappedAction->setCompilerInstance(&CI); + return WrappedAction->BeginSourceFileAction(CI, Filename); +} +void WrapperFrontendAction::ExecuteAction() { + WrappedAction->ExecuteAction(); +} +void WrapperFrontendAction::EndSourceFileAction() { + WrappedAction->EndSourceFileAction(); +} + +bool WrapperFrontendAction::usesPreprocessorOnly() const { + return WrappedAction->usesPreprocessorOnly(); +} +TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() { + return WrappedAction->getTranslationUnitKind(); +} +bool WrapperFrontendAction::hasPCHSupport() const { + return WrappedAction->hasPCHSupport(); +} +bool WrapperFrontendAction::hasASTFileSupport() const { + return WrappedAction->hasASTFileSupport(); +} +bool WrapperFrontendAction::hasIRSupport() const { + return WrappedAction->hasIRSupport(); +} +bool WrapperFrontendAction::hasCodeCompletionSupport() const { + return WrappedAction->hasCodeCompletionSupport(); +} + +WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) + : WrappedAction(WrappedAction) {} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp new file mode 100644 index 0000000..701ef02 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp @@ -0,0 +1,707 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <system_error> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Custom Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +void InitOnlyAction::ExecuteAction() { +} + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) + return CreateASTPrinter(OS, CI.getFrontendOpts().ASTDumpFilter); + return nullptr; +} + +std::unique_ptr<ASTConsumer> +ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter, + CI.getFrontendOpts().ASTDumpDecls, + CI.getFrontendOpts().ASTDumpLookups); +} + +std::unique_ptr<ASTConsumer> +ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTDeclNodeLister(); +} + +std::unique_ptr<ASTConsumer> +ASTViewAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTViewer(); +} + +std::unique_ptr<ASTConsumer> +DeclContextPrintAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateDeclContextPrinter(); +} + +std::unique_ptr<ASTConsumer> +GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = nullptr; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return nullptr; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + return llvm::make_unique<PCHGenerator>(CI.getPreprocessor(), OutputFile, + nullptr, Sysroot, OS); +} + +bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + Sysroot = CI.getHeaderSearchOpts().Sysroot; + if (CI.getFrontendOpts().RelocatablePCH && Sysroot.empty()) { + CI.getDiagnostics().Report(diag::err_relocatable_without_isysroot); + return true; + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + +std::unique_ptr<ASTConsumer> +GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = nullptr; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return nullptr; + + return llvm::make_unique<PCHGenerator>(CI.getPreprocessor(), OutputFile, + Module, Sysroot, OS); +} + +static SmallVectorImpl<char> & +operator+=(SmallVectorImpl<char> &Includes, StringRef RHS) { + Includes.append(RHS.begin(), RHS.end()); + return Includes; +} + +static std::error_code addHeaderInclude(StringRef HeaderName, + SmallVectorImpl<char> &Includes, + const LangOptions &LangOpts, + bool IsExternC) { + if (IsExternC && LangOpts.CPlusPlus) + Includes += "extern \"C\" {\n"; + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + + Includes += HeaderName; + + Includes += "\"\n"; + if (IsExternC && LangOpts.CPlusPlus) + Includes += "}\n"; + return std::error_code(); +} + +static std::error_code addHeaderInclude(const FileEntry *Header, + SmallVectorImpl<char> &Includes, + const LangOptions &LangOpts, + bool IsExternC) { + // Use an absolute path if we don't have a filename as written in the module + // map file; this ensures that we will identify the right file independent of + // header search paths. + if (llvm::sys::path::is_absolute(Header->getName())) + return addHeaderInclude(Header->getName(), Includes, LangOpts, IsExternC); + + SmallString<256> AbsName(Header->getName()); + if (std::error_code Err = llvm::sys::fs::make_absolute(AbsName)) + return Err; + return addHeaderInclude(AbsName, Includes, LangOpts, IsExternC); +} + +/// \brief Collect the set of header includes needed to construct the given +/// module and update the TopHeaders file set of the module. +/// +/// \param Module The module we're collecting includes from. +/// +/// \param Includes Will be augmented with the set of \#includes or \#imports +/// needed to load all of the named headers. +static std::error_code +collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, + ModuleMap &ModMap, clang::Module *Module, + SmallVectorImpl<char> &Includes) { + // Don't collect any headers for unavailable modules. + if (!Module->isAvailable()) + return std::error_code(); + + // Add includes for each of these headers. + for (Module::Header &H : Module->Headers[Module::HK_Normal]) { + Module->addTopHeader(H.Entry); + // Use the path as specified in the module map file. We'll look for this + // file relative to the module build directory (the directory containing + // the module map file) so this will find the same file that we found + // while parsing the module map. + if (std::error_code Err = addHeaderInclude(H.NameAsWritten, Includes, + LangOpts, Module->IsExternC)) + return Err; + } + // Note that Module->PrivateHeaders will not be a TopHeader. + + if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader()) { + // FIXME: Track the name as written here. + Module->addTopHeader(UmbrellaHeader); + if (Module->Parent) { + // Include the umbrella header for submodules. + if (std::error_code Err = addHeaderInclude(UmbrellaHeader, Includes, + LangOpts, Module->IsExternC)) + return Err; + } + } else if (const DirectoryEntry *UmbrellaDir = Module->getUmbrellaDir()) { + // Add all of the headers we find in this subdirectory. + std::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(UmbrellaDir->getName(), DirNative); + for (llvm::sys::fs::recursive_directory_iterator Dir(DirNative.str(), EC), + DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this entry has an extension typically associated with + // headers. + if (!llvm::StringSwitch<bool>(llvm::sys::path::extension(Dir->path())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + const FileEntry *Header = FileMgr.getFile(Dir->path()); + // FIXME: This shouldn't happen unless there is a file system race. Is + // that worth diagnosing? + if (!Header) + continue; + + // If this header is marked 'unavailable' in this module, don't include + // it. + if (ModMap.isHeaderUnavailableInModule(Header, Module)) + continue; + + // Include this header as part of the umbrella directory. + // FIXME: Track the name as written through to here. + Module->addTopHeader(Header); + if (std::error_code Err = + addHeaderInclude(Header, Includes, LangOpts, Module->IsExternC)) + return Err; + } + + if (EC) + return EC; + } + + // Recurse into submodules. + for (clang::Module::submodule_iterator Sub = Module->submodule_begin(), + SubEnd = Module->submodule_end(); + Sub != SubEnd; ++Sub) + if (std::error_code Err = collectModuleHeaderIncludes( + LangOpts, FileMgr, ModMap, *Sub, Includes)) + return Err; + + return std::error_code(); +} + +bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // Find the module map file. + const FileEntry *ModuleMap = CI.getFileManager().getFile(Filename); + if (!ModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << Filename; + return false; + } + + // Parse the module map file. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + if (HS.loadModuleMapFile(ModuleMap, IsSystem)) + return false; + + if (CI.getLangOpts().CurrentModule.empty()) { + CI.getDiagnostics().Report(diag::err_missing_module_name); + + // FIXME: Eventually, we could consider asking whether there was just + // a single module described in the module map, and use that as a + // default. Then it would be fairly trivial to just "compile" a module + // map with a single module (the common case). + return false; + } + + // If we're being run from the command-line, the module build stack will not + // have been filled in yet, so complete it now in order to allow us to detect + // module cycles. + SourceManager &SourceMgr = CI.getSourceManager(); + if (SourceMgr.getModuleBuildStack().empty()) + SourceMgr.pushModuleBuildStack(CI.getLangOpts().CurrentModule, + FullSourceLoc(SourceLocation(), SourceMgr)); + + // Dig out the module definition. + Module = HS.lookupModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + if (!Module) { + CI.getDiagnostics().Report(diag::err_missing_module) + << CI.getLangOpts().CurrentModule << Filename; + + return false; + } + + // Check whether we can build this module at all. + clang::Module::Requirement Requirement; + clang::Module::UnresolvedHeaderDirective MissingHeader; + if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement, + MissingHeader)) { + if (MissingHeader.FileNameLoc.isValid()) { + CI.getDiagnostics().Report(MissingHeader.FileNameLoc, + diag::err_module_header_missing) + << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else { + CI.getDiagnostics().Report(diag::err_module_unavailable) + << Module->getFullModuleName() + << Requirement.second << Requirement.first; + } + + return false; + } + + if (ModuleMapForUniquing && ModuleMapForUniquing != ModuleMap) { + Module->IsInferred = true; + HS.getModuleMap().setInferredModuleAllowedBy(Module, ModuleMapForUniquing); + } else { + ModuleMapForUniquing = ModuleMap; + } + + FileManager &FileMgr = CI.getFileManager(); + + // Collect the set of #includes we need to build the module. + SmallString<256> HeaderContents; + std::error_code Err = std::error_code(); + if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader()) + // FIXME: Track the file name as written. + Err = addHeaderInclude(UmbrellaHeader, HeaderContents, CI.getLangOpts(), + Module->IsExternC); + if (!Err) + Err = collectModuleHeaderIncludes( + CI.getLangOpts(), FileMgr, + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), Module, + HeaderContents); + + if (Err) { + CI.getDiagnostics().Report(diag::err_module_cannot_create_includes) + << Module->getFullModuleName() << Err.message(); + return false; + } + + // Inform the preprocessor that includes from within the input buffer should + // be resolved relative to the build directory of the module map file. + CI.getPreprocessor().setMainFileDir(Module->Directory); + + std::unique_ptr<llvm::MemoryBuffer> InputBuffer = + llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, + Module::getModuleInputBufferName()); + // Ownership of InputBuffer will be transferred to the SourceManager. + setCurrentInput(FrontendInputFile(InputBuffer.release(), getCurrentFileKind(), + Module->IsSystem)); + return true; +} + +bool GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + // If no output file was provided, figure out where this module would go + // in the module cache. + if (CI.getFrontendOpts().OutputFile.empty()) { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + CI.getFrontendOpts().OutputFile = + HS.getModuleFileName(CI.getLangOpts().CurrentModule, + ModuleMapForUniquing->getName()); + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true, + /*CreateMissingDirectories=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + +std::unique_ptr<ASTConsumer> +SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +VerifyPCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +void VerifyPCHAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + bool Preamble = CI.getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + const std::string &Sysroot = CI.getHeaderSearchOpts().Sysroot; + std::unique_ptr<ASTReader> Reader( + new ASTReader(CI.getPreprocessor(), CI.getASTContext(), + Sysroot.empty() ? "" : Sysroot.c_str(), + /*DisableValidation*/ false, + /*AllowPCHWithCompilerErrors*/ false, + /*AllowConfigurationMismatch*/ true, + /*ValidateSystemInputs*/ true)); + + Reader->ReadAST(getCurrentFile(), + Preamble ? serialization::MK_Preamble + : serialization::MK_PCH, + SourceLocation(), + ASTReader::ARR_ConfigurationMismatch); +} + +namespace { + /// \brief AST reader listener that dumps module information for a module + /// file. + class DumpModuleInfoListener : public ASTReaderListener { + llvm::raw_ostream &Out; + + public: + DumpModuleInfoListener(llvm::raw_ostream &Out) : Out(Out) { } + +#define DUMP_BOOLEAN(Value, Text) \ + Out.indent(4) << Text << ": " << (Value? "Yes" : "No") << "\n" + + bool ReadFullVersionInformation(StringRef FullVersion) override { + Out.indent(2) + << "Generated by " + << (FullVersion == getClangFullRepositoryVersion()? "this" + : "a different") + << " Clang: " << FullVersion << "\n"; + return ASTReaderListener::ReadFullVersionInformation(FullVersion); + } + + void ReadModuleName(StringRef ModuleName) override { + Out.indent(2) << "Module name: " << ModuleName << "\n"; + } + void ReadModuleMapFile(StringRef ModuleMapPath) override { + Out.indent(2) << "Module map file: " << ModuleMapPath << "\n"; + } + + bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain, + bool AllowCompatibleDifferences) override { + Out.indent(2) << "Language options:\n"; +#define LANGOPT(Name, Bits, Default, Description) \ + DUMP_BOOLEAN(LangOpts.Name, Description); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + Out.indent(4) << Description << ": " \ + << static_cast<unsigned>(LangOpts.get##Name()) << "\n"; +#define VALUE_LANGOPT(Name, Bits, Default, Description) \ + Out.indent(4) << Description << ": " << LangOpts.Name << "\n"; +#define BENIGN_LANGOPT(Name, Bits, Default, Description) +#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) +#include "clang/Basic/LangOptions.def" + return false; + } + + bool ReadTargetOptions(const TargetOptions &TargetOpts, + bool Complain) override { + Out.indent(2) << "Target options:\n"; + Out.indent(4) << " Triple: " << TargetOpts.Triple << "\n"; + Out.indent(4) << " CPU: " << TargetOpts.CPU << "\n"; + Out.indent(4) << " ABI: " << TargetOpts.ABI << "\n"; + + if (!TargetOpts.FeaturesAsWritten.empty()) { + Out.indent(4) << "Target features:\n"; + for (unsigned I = 0, N = TargetOpts.FeaturesAsWritten.size(); + I != N; ++I) { + Out.indent(6) << TargetOpts.FeaturesAsWritten[I] << "\n"; + } + } + + return false; + } + + virtual bool + ReadDiagnosticOptions(IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, + bool Complain) override { + Out.indent(2) << "Diagnostic options:\n"; +#define DIAGOPT(Name, Bits, Default) DUMP_BOOLEAN(DiagOpts->Name, #Name); +#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + Out.indent(4) << #Name << ": " << DiagOpts->get##Name() << "\n"; +#define VALUE_DIAGOPT(Name, Bits, Default) \ + Out.indent(4) << #Name << ": " << DiagOpts->Name << "\n"; +#include "clang/Basic/DiagnosticOptions.def" + + Out.indent(4) << "Diagnostic flags:\n"; + for (const std::string &Warning : DiagOpts->Warnings) + Out.indent(6) << "-W" << Warning << "\n"; + for (const std::string &Remark : DiagOpts->Remarks) + Out.indent(6) << "-R" << Remark << "\n"; + + return false; + } + + bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, + bool Complain) override { + Out.indent(2) << "Header search options:\n"; + Out.indent(4) << "System root [-isysroot=]: '" << HSOpts.Sysroot << "'\n"; + DUMP_BOOLEAN(HSOpts.UseBuiltinIncludes, + "Use builtin include directories [-nobuiltininc]"); + DUMP_BOOLEAN(HSOpts.UseStandardSystemIncludes, + "Use standard system include directories [-nostdinc]"); + DUMP_BOOLEAN(HSOpts.UseStandardCXXIncludes, + "Use standard C++ include directories [-nostdinc++]"); + DUMP_BOOLEAN(HSOpts.UseLibcxx, + "Use libc++ (rather than libstdc++) [-stdlib=]"); + return false; + } + + bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, + bool Complain, + std::string &SuggestedPredefines) override { + Out.indent(2) << "Preprocessor options:\n"; + DUMP_BOOLEAN(PPOpts.UsePredefines, + "Uses compiler/target-specific predefines [-undef]"); + DUMP_BOOLEAN(PPOpts.DetailedRecord, + "Uses detailed preprocessing record (for indexing)"); + + if (!PPOpts.Macros.empty()) { + Out.indent(4) << "Predefined macros:\n"; + } + + for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator + I = PPOpts.Macros.begin(), IEnd = PPOpts.Macros.end(); + I != IEnd; ++I) { + Out.indent(6); + if (I->second) + Out << "-U"; + else + Out << "-D"; + Out << I->first << "\n"; + } + return false; + } +#undef DUMP_BOOLEAN + }; +} + +void DumpModuleInfoAction::ExecuteAction() { + // Set up the output file. + std::unique_ptr<llvm::raw_fd_ostream> OutFile; + StringRef OutputFileName = getCompilerInstance().getFrontendOpts().OutputFile; + if (!OutputFileName.empty() && OutputFileName != "-") { + std::error_code EC; + OutFile.reset(new llvm::raw_fd_ostream(OutputFileName.str(), EC, + llvm::sys::fs::F_Text)); + } + llvm::raw_ostream &Out = OutFile.get()? *OutFile.get() : llvm::outs(); + + Out << "Information for module file '" << getCurrentFile() << "':\n"; + DumpModuleInfoListener Listener(Out); + ASTReader::readASTFileControlBlock(getCurrentFile(), + getCompilerInstance().getFileManager(), + Listener); +} + +//===----------------------------------------------------------------------===// +// Preprocessor Actions +//===----------------------------------------------------------------------===// + +void DumpRawTokensAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + SourceManager &SM = PP.getSourceManager(); + + // Start lexing the specified input file. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); + RawLex.SetKeepWhitespaceMode(true); + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + PP.DumpToken(RawTok, true); + llvm::errs() << "\n"; + RawLex.LexFromRawLexer(RawTok); + } +} + +void DumpTokensAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + // Start preprocessing the specified input file. + Token Tok; + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + PP.DumpToken(Tok, true); + llvm::errs() << "\n"; + } while (Tok.isNot(tok::eof)); +} + +void GeneratePTHAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + if (CI.getFrontendOpts().OutputFile.empty() || + CI.getFrontendOpts().OutputFile == "-") { + // FIXME: Don't fail this way. + // FIXME: Verify that we can actually seek in the given file. + llvm::report_fatal_error("PTH requires a seekable file for output!"); + } + llvm::raw_fd_ostream *OS = + CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + CacheTokens(CI.getPreprocessor(), OS); +} + +void PreprocessOnlyAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + + // Ignore unknown pragmas. + PP.IgnorePragmas(); + + Token Tok; + // Start parsing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); +} + +void PrintPreprocessedAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + // Output file may need to be set to 'Binary', to avoid converting Unix style + // line feeds (<LF>) to Microsoft style line feeds (<CR><LF>). + // + // Look to see what type of line endings the file uses. If there's a + // CRLF, then we won't open the file up in binary mode. If there is + // just an LF or CR, then we will open the file up in binary mode. + // In this fashion, the output format should match the input format, unless + // the input format has inconsistent line endings. + // + // This should be a relatively fast operation since most files won't have + // all of their source code on a single line. However, that is still a + // concern, so if we scan for too long, we'll just assume the file should + // be opened in binary mode. + bool BinaryMode = true; + bool InvalidFile = false; + const SourceManager& SM = CI.getSourceManager(); + const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getMainFileID(), + &InvalidFile); + if (!InvalidFile) { + const char *cur = Buffer->getBufferStart(); + const char *end = Buffer->getBufferEnd(); + const char *next = (cur != end) ? cur + 1 : end; + + // Limit ourselves to only scanning 256 characters into the source + // file. This is mostly a sanity check in case the file has no + // newlines whatsoever. + if (end - cur > 256) end = cur + 256; + + while (next < end) { + if (*cur == 0x0D) { // CR + if (*next == 0x0A) // CRLF + BinaryMode = false; + + break; + } else if (*cur == 0x0A) // LF + break; + + ++cur, ++next; + } + } + + raw_ostream *OS = CI.createDefaultOutputFile(BinaryMode, getCurrentFile()); + if (!OS) return; + + DoPrintPreprocessedInput(CI.getPreprocessor(), OS, + CI.getPreprocessorOutputOpts()); +} + +void PrintPreambleAction::ExecuteAction() { + switch (getCurrentFileKind()) { + case IK_C: + case IK_CXX: + case IK_ObjC: + case IK_ObjCXX: + case IK_OpenCL: + case IK_CUDA: + break; + + case IK_None: + case IK_Asm: + case IK_PreprocessedC: + case IK_PreprocessedCXX: + case IK_PreprocessedObjC: + case IK_PreprocessedObjCXX: + case IK_AST: + case IK_LLVM_IR: + // We can't do anything with these. + return; + } + + CompilerInstance &CI = getCompilerInstance(); + auto Buffer = CI.getFileManager().getBufferForFile(getCurrentFile()); + if (Buffer) { + unsigned Preamble = + Lexer::ComputePreamble((*Buffer)->getBuffer(), CI.getLangOpts()).first; + llvm::outs().write((*Buffer)->getBufferStart(), Preamble); + } +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp new file mode 100644 index 0000000..1869d0c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp @@ -0,0 +1,31 @@ +//===--- FrontendOptions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendOptions.h" +#include "llvm/ADT/StringSwitch.h" +using namespace clang; + +InputKind FrontendOptions::getInputKindForExtension(StringRef Extension) { + return llvm::StringSwitch<InputKind>(Extension) + .Cases("ast", "pcm", IK_AST) + .Case("c", IK_C) + .Cases("S", "s", IK_Asm) + .Case("i", IK_PreprocessedC) + .Case("ii", IK_PreprocessedCXX) + .Case("m", IK_ObjC) + .Case("mi", IK_PreprocessedObjC) + .Cases("mm", "M", IK_ObjCXX) + .Case("mii", IK_PreprocessedObjCXX) + .Cases("C", "cc", "cp", IK_CXX) + .Cases("cpp", "CPP", "c++", "cxx", "hpp", IK_CXX) + .Case("cl", IK_OpenCL) + .Case("cu", IK_CUDA) + .Cases("ll", "bc", IK_LLVM_IR) + .Default(IK_C); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp new file mode 100644 index 0000000..2701194 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -0,0 +1,138 @@ +//===--- HeaderIncludes.cpp - Generate Header Includes --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +namespace { +class HeaderIncludesCallback : public PPCallbacks { + SourceManager &SM; + raw_ostream *OutputFile; + unsigned CurrentIncludeDepth; + bool HasProcessedPredefines; + bool OwnsOutputFile; + bool ShowAllHeaders; + bool ShowDepth; + bool MSStyle; + +public: + HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_, + raw_ostream *OutputFile_, bool OwnsOutputFile_, + bool ShowDepth_, bool MSStyle_) + : SM(PP->getSourceManager()), OutputFile(OutputFile_), + CurrentIncludeDepth(0), HasProcessedPredefines(false), + OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_), + ShowDepth(ShowDepth_), MSStyle(MSStyle_) {} + + ~HeaderIncludesCallback() { + if (OwnsOutputFile) + delete OutputFile; + } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; +}; +} + +void clang::AttachHeaderIncludeGen(Preprocessor &PP, bool ShowAllHeaders, + StringRef OutputPath, bool ShowDepth, + bool MSStyle) { + raw_ostream *OutputFile = MSStyle ? &llvm::outs() : &llvm::errs(); + bool OwnsOutputFile = false; + + // Open the output file, if used. + if (!OutputPath.empty()) { + std::error_code EC; + llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream( + OutputPath.str(), EC, llvm::sys::fs::F_Append | llvm::sys::fs::F_Text); + if (EC) { + PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure) + << EC.message(); + delete OS; + } else { + OS->SetUnbuffered(); + OS->SetUseAtomicWrites(true); + OutputFile = OS; + OwnsOutputFile = true; + } + } + + PP.addPPCallbacks(llvm::make_unique<HeaderIncludesCallback>(&PP, + ShowAllHeaders, + OutputFile, + OwnsOutputFile, + ShowDepth, + MSStyle)); +} + +void HeaderIncludesCallback::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID PrevFID) { + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + PresumedLoc UserLoc = SM.getPresumedLoc(Loc); + if (UserLoc.isInvalid()) + return; + + // Adjust the current include depth. + if (Reason == PPCallbacks::EnterFile) { + ++CurrentIncludeDepth; + } else if (Reason == PPCallbacks::ExitFile) { + if (CurrentIncludeDepth) + --CurrentIncludeDepth; + + // We track when we are done with the predefines by watching for the first + // place where we drop back to a nesting depth of 1. + if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) + HasProcessedPredefines = true; + + return; + } else + return; + + // Show the header if we are (a) past the predefines, or (b) showing all + // headers and in the predefines at a depth past the initial file and command + // line buffers. + bool ShowHeader = (HasProcessedPredefines || + (ShowAllHeaders && CurrentIncludeDepth > 2)); + + // Dump the header include information we are past the predefines buffer or + // are showing all headers. + if (ShowHeader && Reason == PPCallbacks::EnterFile) { + // Write to a temporary string to avoid unnecessary flushing on errs(). + SmallString<512> Filename(UserLoc.getFilename()); + if (!MSStyle) + Lexer::Stringify(Filename); + + SmallString<256> Msg; + if (MSStyle) + Msg += "Note: including file:"; + + if (ShowDepth) { + // The main source file is at depth 1, so skip one dot. + for (unsigned i = 1; i != CurrentIncludeDepth; ++i) + Msg += MSStyle ? ' ' : '.'; + + if (!MSStyle) + Msg += ' '; + } + Msg += Filename; + Msg += '\n'; + + OutputFile->write(Msg.data(), Msg.size()); + OutputFile->flush(); + } +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp new file mode 100644 index 0000000..a518a0a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp @@ -0,0 +1,707 @@ +//===--- InitHeaderSearch.cpp - Initialize header search paths ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the InitHeaderSearch class. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Config/config.h" // C_INCLUDE_DIRS +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::frontend; + +namespace { + +/// InitHeaderSearch - This class makes it easier to set the search paths of +/// a HeaderSearch object. InitHeaderSearch stores several search path lists +/// internally, which can be sent to a HeaderSearch object in one swoop. +class InitHeaderSearch { + std::vector<std::pair<IncludeDirGroup, DirectoryLookup> > IncludePath; + typedef std::vector<std::pair<IncludeDirGroup, + DirectoryLookup> >::const_iterator path_iterator; + std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes; + HeaderSearch &Headers; + bool Verbose; + std::string IncludeSysroot; + bool HasSysroot; + +public: + + InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot) + : Headers(HS), Verbose(verbose), IncludeSysroot(sysroot), + HasSysroot(!(sysroot.empty() || sysroot == "/")) { + } + + /// AddPath - Add the specified path to the specified group list, prefixing + /// the sysroot if used. + void AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework); + + /// AddUnmappedPath - Add the specified path to the specified group list, + /// without performing any sysroot remapping. + void AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework); + + /// AddSystemHeaderPrefix - Add the specified prefix to the system header + /// prefix list. + void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) { + SystemHeaderPrefixes.push_back(std::make_pair(Prefix, IsSystemHeader)); + } + + /// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu + /// libstdc++. + void AddGnuCPlusPlusIncludePaths(StringRef Base, + StringRef ArchDir, + StringRef Dir32, + StringRef Dir64, + const llvm::Triple &triple); + + /// AddMinGWCPlusPlusIncludePaths - Add the necessary paths to support a MinGW + /// libstdc++. + void AddMinGWCPlusPlusIncludePaths(StringRef Base, + StringRef Arch, + StringRef Version); + + /// AddMinGW64CXXPaths - Add the necessary paths to support + /// libstdc++ of x86_64-w64-mingw32 aka mingw-w64. + void AddMinGW64CXXPaths(StringRef Base, + StringRef Version); + + // AddDefaultCIncludePaths - Add paths that should always be searched. + void AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); + + // AddDefaultCPlusPlusIncludePaths - Add paths that should be searched when + // compiling c++. + void AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); + + /// AddDefaultSystemIncludePaths - Adds the default system include paths so + /// that e.g. stdio.h is found. + void AddDefaultIncludePaths(const LangOptions &Lang, + const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); + + /// Realize - Merges all search path lists into one list and send it to + /// HeaderSearch. + void Realize(const LangOptions &Lang); +}; + +} // end anonymous namespace. + +static bool CanPrefixSysroot(StringRef Path) { +#if defined(LLVM_ON_WIN32) + return !Path.empty() && llvm::sys::path::is_separator(Path[0]); +#else + return llvm::sys::path::is_absolute(Path); +#endif +} + +void InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework) { + // Add the path with sysroot prepended, if desired and this is a system header + // group. + if (HasSysroot) { + SmallString<256> MappedPathStorage; + StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); + if (CanPrefixSysroot(MappedPathStr)) { + AddUnmappedPath(IncludeSysroot + Path, Group, isFramework); + return; + } + } + + AddUnmappedPath(Path, Group, isFramework); +} + +void InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework) { + assert(!Path.isTriviallyEmpty() && "can't handle empty path here"); + + FileManager &FM = Headers.getFileMgr(); + SmallString<256> MappedPathStorage; + StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); + + // Compute the DirectoryLookup type. + SrcMgr::CharacteristicKind Type; + if (Group == Quoted || Group == Angled || Group == IndexHeaderMap) { + Type = SrcMgr::C_User; + } else if (Group == ExternCSystem) { + Type = SrcMgr::C_ExternCSystem; + } else { + Type = SrcMgr::C_System; + } + + // If the directory exists, add it. + if (const DirectoryEntry *DE = FM.getDirectory(MappedPathStr)) { + IncludePath.push_back( + std::make_pair(Group, DirectoryLookup(DE, Type, isFramework))); + return; + } + + // Check to see if this is an apple-style headermap (which are not allowed to + // be frameworks). + if (!isFramework) { + if (const FileEntry *FE = FM.getFile(MappedPathStr)) { + if (const HeaderMap *HM = Headers.CreateHeaderMap(FE)) { + // It is a headermap, add it to the search path. + IncludePath.push_back( + std::make_pair(Group, + DirectoryLookup(HM, Type, Group == IndexHeaderMap))); + return; + } + } + } + + if (Verbose) + llvm::errs() << "ignoring nonexistent directory \"" + << MappedPathStr << "\"\n"; +} + +void InitHeaderSearch::AddGnuCPlusPlusIncludePaths(StringRef Base, + StringRef ArchDir, + StringRef Dir32, + StringRef Dir64, + const llvm::Triple &triple) { + // Add the base dir + AddPath(Base, CXXSystem, false); + + // Add the multilib dirs + llvm::Triple::ArchType arch = triple.getArch(); + bool is64bit = arch == llvm::Triple::ppc64 || arch == llvm::Triple::x86_64; + if (is64bit) + AddPath(Base + "/" + ArchDir + "/" + Dir64, CXXSystem, false); + else + AddPath(Base + "/" + ArchDir + "/" + Dir32, CXXSystem, false); + + // Add the backward dir + AddPath(Base + "/backward", CXXSystem, false); +} + +void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(StringRef Base, + StringRef Arch, + StringRef Version) { + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++", + CXXSystem, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch, + CXXSystem, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward", + CXXSystem, false); +} + +void InitHeaderSearch::AddMinGW64CXXPaths(StringRef Base, + StringRef Version) { + // Assumes Base is HeaderSearchOpts' ResourceDir + AddPath(Base + "/../../../include/c++/" + Version, + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/x86_64-w64-mingw32", + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/i686-w64-mingw32", + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/backward", + CXXSystem, false); +} + +void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + + if (HSOpts.UseStandardSystemIncludes) { + switch (os) { + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + case llvm::Triple::Bitrig: + break; + default: + // FIXME: temporary hack: hard-coded paths. + AddPath("/usr/local/include", System, false); + break; + } + } + + // Builtin includes use #include_next directives and should be positioned + // just prior C include dirs. + if (HSOpts.UseBuiltinIncludes) { + // Ignore the sys root, we *always* look for clang headers relative to + // supplied path. + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "include"); + AddUnmappedPath(P.str(), ExternCSystem, false); + } + + // All remaining additions are for system include directories, early exit if + // we aren't using them. + if (!HSOpts.UseStandardSystemIncludes) + return; + + // Add dirs specified via 'configure --with-c-include-dirs'. + StringRef CIncludeDirs(C_INCLUDE_DIRS); + if (CIncludeDirs != "") { + SmallVector<StringRef, 5> dirs; + CIncludeDirs.split(dirs, ":"); + for (SmallVectorImpl<StringRef>::iterator i = dirs.begin(); + i != dirs.end(); + ++i) + AddPath(*i, ExternCSystem, false); + return; + } + + switch (os) { + case llvm::Triple::Linux: + llvm_unreachable("Include management is handled in the driver."); + + case llvm::Triple::Haiku: + AddPath("/boot/common/include", System, false); + AddPath("/boot/develop/headers/os", System, false); + AddPath("/boot/develop/headers/os/app", System, false); + AddPath("/boot/develop/headers/os/arch", System, false); + AddPath("/boot/develop/headers/os/device", System, false); + AddPath("/boot/develop/headers/os/drivers", System, false); + AddPath("/boot/develop/headers/os/game", System, false); + AddPath("/boot/develop/headers/os/interface", System, false); + AddPath("/boot/develop/headers/os/kernel", System, false); + AddPath("/boot/develop/headers/os/locale", System, false); + AddPath("/boot/develop/headers/os/mail", System, false); + AddPath("/boot/develop/headers/os/media", System, false); + AddPath("/boot/develop/headers/os/midi", System, false); + AddPath("/boot/develop/headers/os/midi2", System, false); + AddPath("/boot/develop/headers/os/net", System, false); + AddPath("/boot/develop/headers/os/storage", System, false); + AddPath("/boot/develop/headers/os/support", System, false); + AddPath("/boot/develop/headers/os/translation", System, false); + AddPath("/boot/develop/headers/os/add-ons/graphics", System, false); + AddPath("/boot/develop/headers/os/add-ons/input_server", System, false); + AddPath("/boot/develop/headers/os/add-ons/screen_saver", System, false); + AddPath("/boot/develop/headers/os/add-ons/tracker", System, false); + AddPath("/boot/develop/headers/os/be_apps/Deskbar", System, false); + AddPath("/boot/develop/headers/os/be_apps/NetPositive", System, false); + AddPath("/boot/develop/headers/os/be_apps/Tracker", System, false); + AddPath("/boot/develop/headers/cpp", System, false); + AddPath("/boot/develop/headers/cpp/i586-pc-haiku", System, false); + AddPath("/boot/develop/headers/3rdparty", System, false); + AddPath("/boot/develop/headers/bsd", System, false); + AddPath("/boot/develop/headers/glibc", System, false); + AddPath("/boot/develop/headers/posix", System, false); + AddPath("/boot/develop/headers", System, false); + break; + case llvm::Triple::RTEMS: + break; + case llvm::Triple::Win32: + switch (triple.getEnvironment()) { + default: llvm_unreachable("Include management is handled in the driver."); + case llvm::Triple::Cygnus: + AddPath("/usr/include/w32api", System, false); + break; + case llvm::Triple::GNU: + // mingw-w64 crt include paths + // <sysroot>/i686-w64-mingw32/include + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "../../../i686-w64-mingw32/include"); + AddPath(P.str(), System, false); + + // <sysroot>/x86_64-w64-mingw32/include + P.resize(HSOpts.ResourceDir.size()); + llvm::sys::path::append(P, "../../../x86_64-w64-mingw32/include"); + AddPath(P.str(), System, false); + + // mingw.org crt include paths + // <sysroot>/include + P.resize(HSOpts.ResourceDir.size()); + llvm::sys::path::append(P, "../../../include"); + AddPath(P.str(), System, false); + AddPath("/mingw/include", System, false); +#if defined(LLVM_ON_WIN32) + AddPath("c:/mingw/include", System, false); +#endif + break; + } + break; + default: + break; + } + + if ( os != llvm::Triple::RTEMS ) + AddPath("/usr/include", ExternCSystem, false); +} + +void InitHeaderSearch:: +AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + // FIXME: temporary hack: hard-coded paths. + + if (triple.isOSDarwin()) { + switch (triple.getArch()) { + default: break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "powerpc-apple-darwin10", "", "ppc64", + triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0", + "powerpc-apple-darwin10", "", "ppc64", + triple); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "i686-apple-darwin10", "", "x86_64", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0", + "i686-apple-darwin8", "", "", triple); + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "arm-apple-darwin10", "v7", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "arm-apple-darwin10", "v6", "", triple); + break; + + case llvm::Triple::aarch64: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "arm64-apple-darwin10", "", "", triple); + break; + } + return; + } + + switch (os) { + case llvm::Triple::Linux: + llvm_unreachable("Include management is handled in the driver."); + break; + case llvm::Triple::Win32: + switch (triple.getEnvironment()) { + default: llvm_unreachable("Include management is handled in the driver."); + case llvm::Triple::Cygnus: + // Cygwin-1.7 + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.7.3"); + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3"); + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4"); + // g++-4 / Cygwin-1.5 + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.2"); + break; + case llvm::Triple::GNU: + // mingw-w64 C++ include paths (i686-w64-mingw32 and x86_64-w64-mingw32) + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.3"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.8.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.8.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.8.2"); + // mingw.org C++ include paths +#if defined(LLVM_ON_WIN32) + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.7.0"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.7.1"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.7.2"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.7.3"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.8.0"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.8.1"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.8.2"); +#endif + break; + } + case llvm::Triple::DragonFly: + if (llvm::sys::fs::exists("/usr/lib/gcc47")) + AddPath("/usr/include/c++/4.7", CXXSystem, false); + else + AddPath("/usr/include/c++/4.4", CXXSystem, false); + break; + case llvm::Triple::OpenBSD: { + std::string t = triple.getTriple(); + if (t.substr(0, 6) == "x86_64") + t.replace(0, 6, "amd64"); + AddGnuCPlusPlusIncludePaths("/usr/include/g++", + t, "", "", triple); + break; + } + case llvm::Triple::Minix: + AddGnuCPlusPlusIncludePaths("/usr/gnu/include/c++/4.4.3", + "", "", "", triple); + break; + case llvm::Triple::Solaris: + AddGnuCPlusPlusIncludePaths("/usr/gcc/4.5/include/c++/4.5.2/", + "i386-pc-solaris2.11", "", "", triple); + break; + default: + break; + } +} + +void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang, + const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + // NB: This code path is going away. All of the logic is moving into the + // driver which has the information necessary to do target-specific + // selections of default include paths. Each target which moves there will be + // exempted from this logic here until we can delete the entire pile of code. + switch (triple.getOS()) { + default: + break; // Everything else continues to use this routine's logic. + + case llvm::Triple::Linux: + return; + + case llvm::Triple::Win32: + if (triple.getEnvironment() == llvm::Triple::MSVC || + triple.getEnvironment() == llvm::Triple::Itanium || + triple.isOSBinFormatMachO()) + return; + break; + } + + if (Lang.CPlusPlus && HSOpts.UseStandardCXXIncludes && + HSOpts.UseStandardSystemIncludes) { + if (HSOpts.UseLibcxx) { + if (triple.isOSDarwin()) { + // On Darwin, libc++ may be installed alongside the compiler in + // include/c++/v1. + if (!HSOpts.ResourceDir.empty()) { + // Remove version from foo/lib/clang/version + StringRef NoVer = llvm::sys::path::parent_path(HSOpts.ResourceDir); + // Remove clang from foo/lib/clang + StringRef Lib = llvm::sys::path::parent_path(NoVer); + // Remove lib from foo/lib + SmallString<128> P = llvm::sys::path::parent_path(Lib); + + // Get foo/include/c++/v1 + llvm::sys::path::append(P, "include", "c++", "v1"); + AddUnmappedPath(P.str(), CXXSystem, false); + } + } + // On Solaris, include the support directory for things like xlocale and + // fudged system headers. + if (triple.getOS() == llvm::Triple::Solaris) + AddPath("/usr/include/c++/v1/support/solaris", CXXSystem, false); + + AddPath("/usr/include/c++/v1", CXXSystem, false); + } else { + AddDefaultCPlusPlusIncludePaths(triple, HSOpts); + } + } + + AddDefaultCIncludePaths(triple, HSOpts); + + // Add the default framework include paths on Darwin. + if (HSOpts.UseStandardSystemIncludes) { + if (triple.isOSDarwin()) { + AddPath("/System/Library/Frameworks", System, true); + AddPath("/Library/Frameworks", System, true); + } + } +} + +/// RemoveDuplicates - If there are duplicate directory entries in the specified +/// search list, remove the later (dead) ones. Returns the number of non-system +/// headers removed, which is used to update NumAngled. +static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList, + unsigned First, bool Verbose) { + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs; + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs; + llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps; + unsigned NonSystemRemoved = 0; + for (unsigned i = First; i != SearchList.size(); ++i) { + unsigned DirToRemove = i; + + const DirectoryLookup &CurEntry = SearchList[i]; + + if (CurEntry.isNormalDir()) { + // If this isn't the first time we've seen this dir, remove it. + if (SeenDirs.insert(CurEntry.getDir()).second) + continue; + } else if (CurEntry.isFramework()) { + // If this isn't the first time we've seen this framework dir, remove it. + if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir()).second) + continue; + } else { + assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); + // If this isn't the first time we've seen this headermap, remove it. + if (SeenHeaderMaps.insert(CurEntry.getHeaderMap()).second) + continue; + } + + // If we have a normal #include dir/framework/headermap that is shadowed + // later in the chain by a system include location, we actually want to + // ignore the user's request and drop the user dir... keeping the system + // dir. This is weird, but required to emulate GCC's search path correctly. + // + // Since dupes of system dirs are rare, just rescan to find the original + // that we're nuking instead of using a DenseMap. + if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) { + // Find the dir that this is the same of. + unsigned FirstDir; + for (FirstDir = 0; ; ++FirstDir) { + assert(FirstDir != i && "Didn't find dupe?"); + + const DirectoryLookup &SearchEntry = SearchList[FirstDir]; + + // If these are different lookup types, then they can't be the dupe. + if (SearchEntry.getLookupType() != CurEntry.getLookupType()) + continue; + + bool isSame; + if (CurEntry.isNormalDir()) + isSame = SearchEntry.getDir() == CurEntry.getDir(); + else if (CurEntry.isFramework()) + isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir(); + else { + assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); + isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap(); + } + + if (isSame) + break; + } + + // If the first dir in the search path is a non-system dir, zap it + // instead of the system one. + if (SearchList[FirstDir].getDirCharacteristic() == SrcMgr::C_User) + DirToRemove = FirstDir; + } + + if (Verbose) { + llvm::errs() << "ignoring duplicate directory \"" + << CurEntry.getName() << "\"\n"; + if (DirToRemove != i) + llvm::errs() << " as it is a non-system directory that duplicates " + << "a system directory\n"; + } + if (DirToRemove != i) + ++NonSystemRemoved; + + // This is reached if the current entry is a duplicate. Remove the + // DirToRemove (usually the current dir). + SearchList.erase(SearchList.begin()+DirToRemove); + --i; + } + return NonSystemRemoved; +} + + +void InitHeaderSearch::Realize(const LangOptions &Lang) { + // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList. + std::vector<DirectoryLookup> SearchList; + SearchList.reserve(IncludePath.size()); + + // Quoted arguments go first. + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == Quoted) + SearchList.push_back(it->second); + } + // Deduplicate and remember index. + RemoveDuplicates(SearchList, 0, Verbose); + unsigned NumQuoted = SearchList.size(); + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == Angled || it->first == IndexHeaderMap) + SearchList.push_back(it->second); + } + + RemoveDuplicates(SearchList, NumQuoted, Verbose); + unsigned NumAngled = SearchList.size(); + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == System || it->first == ExternCSystem || + (!Lang.ObjC1 && !Lang.CPlusPlus && it->first == CSystem) || + (/*FIXME !Lang.ObjC1 && */Lang.CPlusPlus && it->first == CXXSystem) || + (Lang.ObjC1 && !Lang.CPlusPlus && it->first == ObjCSystem) || + (Lang.ObjC1 && Lang.CPlusPlus && it->first == ObjCXXSystem)) + SearchList.push_back(it->second); + } + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == After) + SearchList.push_back(it->second); + } + + // Remove duplicates across both the Angled and System directories. GCC does + // this and failing to remove duplicates across these two groups breaks + // #include_next. + unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose); + NumAngled -= NonSystemRemoved; + + bool DontSearchCurDir = false; // TODO: set to true if -I- is set? + Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir); + + Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes); + + // If verbose, print the list of directories that will be searched. + if (Verbose) { + llvm::errs() << "#include \"...\" search starts here:\n"; + for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { + if (i == NumQuoted) + llvm::errs() << "#include <...> search starts here:\n"; + const char *Name = SearchList[i].getName(); + const char *Suffix; + if (SearchList[i].isNormalDir()) + Suffix = ""; + else if (SearchList[i].isFramework()) + Suffix = " (framework directory)"; + else { + assert(SearchList[i].isHeaderMap() && "Unknown DirectoryLookup"); + Suffix = " (headermap)"; + } + llvm::errs() << " " << Name << Suffix << "\n"; + } + llvm::errs() << "End of search list.\n"; + } +} + +void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, + const HeaderSearchOptions &HSOpts, + const LangOptions &Lang, + const llvm::Triple &Triple) { + InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot); + + // Add the user defined entries. + for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) { + const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i]; + if (E.IgnoreSysRoot) { + Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework); + } else { + Init.AddPath(E.Path, E.Group, E.IsFramework); + } + } + + Init.AddDefaultIncludePaths(Lang, Triple, HSOpts); + + for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i) + Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix, + HSOpts.SystemHeaderPrefixes[i].IsSystemHeader); + + if (HSOpts.UseBuiltinIncludes) { + // Set up the builtin include directory in the module map. + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "include"); + if (const DirectoryEntry *Dir = HS.getFileMgr().getDirectory(P.str())) + HS.getModuleMap().setBuiltinIncludeDir(Dir); + } + + Init.Realize(Lang); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp new file mode 100644 index 0000000..f4241a9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -0,0 +1,973 @@ +//===--- InitPreprocessor.cpp - PP initialization code. ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the clang::InitializePreprocessor function. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +using namespace clang; + +static bool MacroBodyEndsInBackslash(StringRef MacroBody) { + while (!MacroBody.empty() && isWhitespace(MacroBody.back())) + MacroBody = MacroBody.drop_back(); + return !MacroBody.empty() && MacroBody.back() == '\\'; +} + +// 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(MacroBuilder &Builder, StringRef Macro, + DiagnosticsEngine &Diags) { + std::pair<StringRef, StringRef> MacroPair = Macro.split('='); + StringRef MacroName = MacroPair.first; + StringRef MacroBody = MacroPair.second; + if (MacroName.size() != Macro.size()) { + // Per GCC -D semantics, the macro ends at \n if it exists. + StringRef::size_type End = MacroBody.find_first_of("\n\r"); + if (End != StringRef::npos) + Diags.Report(diag::warn_fe_macro_contains_embedded_newline) + << MacroName; + MacroBody = MacroBody.substr(0, End); + // We handle macro bodies which end in a backslash by appending an extra + // backslash+newline. This makes sure we don't accidentally treat the + // backslash as a line continuation marker. + if (MacroBodyEndsInBackslash(MacroBody)) + Builder.defineMacro(MacroName, Twine(MacroBody) + "\\\n"); + else + Builder.defineMacro(MacroName, MacroBody); + } else { + // Push "macroname 1". + Builder.defineMacro(Macro); + } +} + +/// AddImplicitInclude - Add an implicit \#include of the specified file to the +/// predefines buffer. +/// As these includes are generated by -include arguments the header search +/// logic is going to search relatively to the current working directory. +static void AddImplicitInclude(MacroBuilder &Builder, StringRef File) { + Builder.append(Twine("#include \"") + File + "\""); +} + +static void AddImplicitIncludeMacros(MacroBuilder &Builder, StringRef File) { + Builder.append(Twine("#__include_macros \"") + File + "\""); + // Marker token to stop the __include_macros fetch loop. + Builder.append("##"); // ##? +} + +/// AddImplicitIncludePTH - Add an implicit \#include using the original file +/// used to generate a PTH cache. +static void AddImplicitIncludePTH(MacroBuilder &Builder, Preprocessor &PP, + StringRef ImplicitIncludePTH) { + PTHManager *P = PP.getPTHManager(); + // Null check 'P' in the corner case where it couldn't be created. + const char *OriginalFile = P ? P->getOriginalSourceFile() : nullptr; + + if (!OriginalFile) { + PP.getDiagnostics().Report(diag::err_fe_pth_file_has_no_source_header) + << ImplicitIncludePTH; + return; + } + + AddImplicitInclude(Builder, OriginalFile); +} + +/// \brief Add an implicit \#include using the original file used to generate +/// a PCH file. +static void AddImplicitIncludePCH(MacroBuilder &Builder, Preprocessor &PP, + StringRef ImplicitIncludePCH) { + std::string OriginalFile = + ASTReader::getOriginalSourceFile(ImplicitIncludePCH, PP.getFileManager(), + PP.getDiagnostics()); + if (OriginalFile.empty()) + return; + + AddImplicitInclude(Builder, OriginalFile); +} + +/// PickFP - This is used to pick a value based on the FP semantics of the +/// specified FP model. +template <typename T> +static T PickFP(const llvm::fltSemantics *Sem, T IEEESingleVal, + T IEEEDoubleVal, T X87DoubleExtendedVal, T PPCDoubleDoubleVal, + T IEEEQuadVal) { + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEsingle) + return IEEESingleVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEdouble) + return IEEEDoubleVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::x87DoubleExtended) + return X87DoubleExtendedVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::PPCDoubleDouble) + return PPCDoubleDoubleVal; + assert(Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEquad); + return IEEEQuadVal; +} + +static void DefineFloatMacros(MacroBuilder &Builder, StringRef Prefix, + const llvm::fltSemantics *Sem, StringRef Ext) { + const char *DenormMin, *Epsilon, *Max, *Min; + DenormMin = PickFP(Sem, "1.40129846e-45", "4.9406564584124654e-324", + "3.64519953188247460253e-4951", + "4.94065645841246544176568792868221e-324", + "6.47517511943802511092443895822764655e-4966"); + int Digits = PickFP(Sem, 6, 15, 18, 31, 33); + Epsilon = PickFP(Sem, "1.19209290e-7", "2.2204460492503131e-16", + "1.08420217248550443401e-19", + "4.94065645841246544176568792868221e-324", + "1.92592994438723585305597794258492732e-34"); + int MantissaDigits = PickFP(Sem, 24, 53, 64, 106, 113); + int Min10Exp = PickFP(Sem, -37, -307, -4931, -291, -4931); + int Max10Exp = PickFP(Sem, 38, 308, 4932, 308, 4932); + int MinExp = PickFP(Sem, -125, -1021, -16381, -968, -16381); + int MaxExp = PickFP(Sem, 128, 1024, 16384, 1024, 16384); + Min = PickFP(Sem, "1.17549435e-38", "2.2250738585072014e-308", + "3.36210314311209350626e-4932", + "2.00416836000897277799610805135016e-292", + "3.36210314311209350626267781732175260e-4932"); + Max = PickFP(Sem, "3.40282347e+38", "1.7976931348623157e+308", + "1.18973149535723176502e+4932", + "1.79769313486231580793728971405301e+308", + "1.18973149535723176508575932662800702e+4932"); + + SmallString<32> DefPrefix; + DefPrefix = "__"; + DefPrefix += Prefix; + DefPrefix += "_"; + + Builder.defineMacro(DefPrefix + "DENORM_MIN__", Twine(DenormMin)+Ext); + Builder.defineMacro(DefPrefix + "HAS_DENORM__"); + Builder.defineMacro(DefPrefix + "DIG__", Twine(Digits)); + Builder.defineMacro(DefPrefix + "EPSILON__", Twine(Epsilon)+Ext); + Builder.defineMacro(DefPrefix + "HAS_INFINITY__"); + Builder.defineMacro(DefPrefix + "HAS_QUIET_NAN__"); + Builder.defineMacro(DefPrefix + "MANT_DIG__", Twine(MantissaDigits)); + + Builder.defineMacro(DefPrefix + "MAX_10_EXP__", Twine(Max10Exp)); + Builder.defineMacro(DefPrefix + "MAX_EXP__", Twine(MaxExp)); + Builder.defineMacro(DefPrefix + "MAX__", Twine(Max)+Ext); + + Builder.defineMacro(DefPrefix + "MIN_10_EXP__","("+Twine(Min10Exp)+")"); + Builder.defineMacro(DefPrefix + "MIN_EXP__", "("+Twine(MinExp)+")"); + Builder.defineMacro(DefPrefix + "MIN__", Twine(Min)+Ext); +} + + +/// DefineTypeSize - Emit a macro to the predefines buffer that declares a macro +/// named MacroName with the max value for a type with width 'TypeWidth' a +/// signedness of 'isSigned' and with a value suffix of 'ValSuffix' (e.g. LL). +static void DefineTypeSize(const Twine &MacroName, unsigned TypeWidth, + StringRef ValSuffix, bool isSigned, + MacroBuilder &Builder) { + llvm::APInt MaxVal = isSigned ? llvm::APInt::getSignedMaxValue(TypeWidth) + : llvm::APInt::getMaxValue(TypeWidth); + Builder.defineMacro(MacroName, MaxVal.toString(10, isSigned) + ValSuffix); +} + +/// DefineTypeSize - An overloaded helper that uses TargetInfo to determine +/// the width, suffix, and signedness of the given type +static void DefineTypeSize(const Twine &MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + DefineTypeSize(MacroName, TI.getTypeWidth(Ty), TI.getTypeConstantSuffix(Ty), + TI.isTypeSigned(Ty), Builder); +} + +static void DefineFmt(const Twine &Prefix, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + bool IsSigned = TI.isTypeSigned(Ty); + StringRef FmtModifier = TI.getTypeFormatModifier(Ty); + for (const char *Fmt = IsSigned ? "di" : "ouxX"; *Fmt; ++Fmt) { + Builder.defineMacro(Prefix + "_FMT" + Twine(*Fmt) + "__", + Twine("\"") + FmtModifier + Twine(*Fmt) + "\""); + } +} + +static void DefineType(const Twine &MacroName, TargetInfo::IntType Ty, + MacroBuilder &Builder) { + Builder.defineMacro(MacroName, TargetInfo::getTypeName(Ty)); +} + +static void DefineTypeWidth(StringRef MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, Twine(TI.getTypeWidth(Ty))); +} + +static void DefineTypeSizeof(StringRef MacroName, unsigned BitWidth, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, + Twine(BitWidth / TI.getCharWidth())); +} + +static void DefineExactWidthIntType(TargetInfo::IntType Ty, + const TargetInfo &TI, + MacroBuilder &Builder) { + int TypeWidth = TI.getTypeWidth(Ty); + bool IsSigned = TI.isTypeSigned(Ty); + + // Use the target specified int64 type, when appropriate, so that [u]int64_t + // ends up being defined in terms of the correct type. + if (TypeWidth == 64) + Ty = IsSigned ? TI.getInt64Type() : TI.getUInt64Type(); + + const char *Prefix = IsSigned ? "__INT" : "__UINT"; + + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); + + StringRef ConstSuffix(TI.getTypeConstantSuffix(Ty)); + Builder.defineMacro(Prefix + Twine(TypeWidth) + "_C_SUFFIX__", ConstSuffix); +} + +static void DefineExactWidthIntTypeSize(TargetInfo::IntType Ty, + const TargetInfo &TI, + MacroBuilder &Builder) { + int TypeWidth = TI.getTypeWidth(Ty); + bool IsSigned = TI.isTypeSigned(Ty); + + // Use the target specified int64 type, when appropriate, so that [u]int64_t + // ends up being defined in terms of the correct type. + if (TypeWidth == 64) + Ty = IsSigned ? TI.getInt64Type() : TI.getUInt64Type(); + + const char *Prefix = IsSigned ? "__INT" : "__UINT"; + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); +} + +static void DefineLeastWidthIntType(unsigned TypeWidth, bool IsSigned, + const TargetInfo &TI, + MacroBuilder &Builder) { + TargetInfo::IntType Ty = TI.getLeastIntTypeByWidth(TypeWidth, IsSigned); + if (Ty == TargetInfo::NoInt) + return; + + const char *Prefix = IsSigned ? "__INT_LEAST" : "__UINT_LEAST"; + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); +} + +static void DefineFastIntType(unsigned TypeWidth, bool IsSigned, + const TargetInfo &TI, MacroBuilder &Builder) { + // stdint.h currently defines the fast int types as equivalent to the least + // types. + TargetInfo::IntType Ty = TI.getLeastIntTypeByWidth(TypeWidth, IsSigned); + if (Ty == TargetInfo::NoInt) + return; + + const char *Prefix = IsSigned ? "__INT_FAST" : "__UINT_FAST"; + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); + + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); +} + + +/// Get the value the ATOMIC_*_LOCK_FREE macro should have for a type with +/// the specified properties. +static const char *getLockFreeValue(unsigned TypeWidth, unsigned TypeAlign, + unsigned InlineWidth) { + // Fully-aligned, power-of-2 sizes no larger than the inline + // width will be inlined as lock-free operations. + if (TypeWidth == TypeAlign && (TypeWidth & (TypeWidth - 1)) == 0 && + TypeWidth <= InlineWidth) + return "2"; // "always lock free" + // We cannot be certain what operations the lib calls might be + // able to implement as lock-free on future processors. + return "1"; // "sometimes lock free" +} + +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automated reference counting and libstdc++ (4.2). +static void AddObjCXXARCLibstdcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_GLIBCXX_PREDEFINED_OBJC_ARC_IS_SCALAR"); + + std::string Result; + { + // Provide specializations for the __is_scalar type trait so that + // lifetime-qualified objects are not considered "scalar" types, which + // libstdc++ uses as an indicator of the presence of trivial copy, assign, + // default-construct, and destruct semantics (none of which hold for + // lifetime-qualified objects in ARC). + llvm::raw_string_ostream Out(Result); + + Out << "namespace std {\n" + << "\n" + << "struct __true_type;\n" + << "struct __false_type;\n" + << "\n"; + + Out << "template<typename _Tp> struct __is_scalar;\n" + << "\n"; + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(strong))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + if (LangOpts.ObjCARCWeak) { + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(weak))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + } + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(autoreleasing)))" + << " _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + Out << "}\n"; + } + Builder.append(Result); +} + +static void InitializeStandardPredefinedMacros(const TargetInfo &TI, + const LangOptions &LangOpts, + const FrontendOptions &FEOpts, + MacroBuilder &Builder) { + if (!LangOpts.MSVCCompat && !LangOpts.TraditionalCPP) + Builder.defineMacro("__STDC__"); + if (LangOpts.Freestanding) + Builder.defineMacro("__STDC_HOSTED__", "0"); + else + Builder.defineMacro("__STDC_HOSTED__"); + + if (!LangOpts.CPlusPlus) { + if (LangOpts.C11) + Builder.defineMacro("__STDC_VERSION__", "201112L"); + else if (LangOpts.C99) + Builder.defineMacro("__STDC_VERSION__", "199901L"); + else if (!LangOpts.GNUMode && LangOpts.Digraphs) + Builder.defineMacro("__STDC_VERSION__", "199409L"); + } else { + // FIXME: Use correct value for C++17. + if (LangOpts.CPlusPlus1z) + Builder.defineMacro("__cplusplus", "201406L"); + // C++1y [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201402L when compiling a + // C++ translation unit. + else if (LangOpts.CPlusPlus14) + Builder.defineMacro("__cplusplus", "201402L"); + // C++11 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201103L when compiling a + // C++ translation unit. + else if (LangOpts.CPlusPlus11) + Builder.defineMacro("__cplusplus", "201103L"); + // C++03 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 199711L when compiling a + // C++ translation unit. + else + Builder.defineMacro("__cplusplus", "199711L"); + } + + // In C11 these are environment macros. In C++11 they are only defined + // as part of <cuchar>. To prevent breakage when mixing C and C++ + // code, define these macros unconditionally. We can define them + // unconditionally, as Clang always uses UTF-16 and UTF-32 for 16-bit + // and 32-bit character literals. + Builder.defineMacro("__STDC_UTF_16__", "1"); + Builder.defineMacro("__STDC_UTF_32__", "1"); + + if (LangOpts.ObjC1) + Builder.defineMacro("__OBJC__"); + + // Not "standard" per se, but available even with the -undef flag. + if (LangOpts.AsmPreprocessor) + Builder.defineMacro("__ASSEMBLER__"); +} + +/// Initialize the predefined C++ language feature test macros defined in +/// ISO/IEC JTC1/SC22/WG21 (C++) SD-6: "SG10 Feature Test Recommendations". +static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, + MacroBuilder &Builder) { + // C++98 features. + if (LangOpts.RTTI) + Builder.defineMacro("__cpp_rtti", "199711"); + if (LangOpts.CXXExceptions) + Builder.defineMacro("__cpp_exceptions", "199711"); + + // C++11 features. + if (LangOpts.CPlusPlus11) { + Builder.defineMacro("__cpp_unicode_characters", "200704"); + Builder.defineMacro("__cpp_raw_strings", "200710"); + Builder.defineMacro("__cpp_unicode_literals", "200710"); + Builder.defineMacro("__cpp_user_defined_literals", "200809"); + Builder.defineMacro("__cpp_lambdas", "200907"); + Builder.defineMacro("__cpp_constexpr", + LangOpts.CPlusPlus14 ? "201304" : "200704"); + Builder.defineMacro("__cpp_range_based_for", "200907"); + Builder.defineMacro("__cpp_static_assert", "200410"); + Builder.defineMacro("__cpp_decltype", "200707"); + Builder.defineMacro("__cpp_attributes", "200809"); + Builder.defineMacro("__cpp_rvalue_references", "200610"); + Builder.defineMacro("__cpp_variadic_templates", "200704"); + Builder.defineMacro("__cpp_initializer_lists", "200806"); + Builder.defineMacro("__cpp_delegating_constructors", "200604"); + Builder.defineMacro("__cpp_nsdmi", "200809"); + Builder.defineMacro("__cpp_inheriting_constructors", "200802"); + Builder.defineMacro("__cpp_ref_qualifiers", "200710"); + Builder.defineMacro("__cpp_alias_templates", "200704"); + } + + // C++14 features. + if (LangOpts.CPlusPlus14) { + Builder.defineMacro("__cpp_binary_literals", "201304"); + Builder.defineMacro("__cpp_digit_separators", "201309"); + Builder.defineMacro("__cpp_init_captures", "201304"); + Builder.defineMacro("__cpp_generic_lambdas", "201304"); + Builder.defineMacro("__cpp_decltype_auto", "201304"); + Builder.defineMacro("__cpp_return_type_deduction", "201304"); + Builder.defineMacro("__cpp_aggregate_nsdmi", "201304"); + Builder.defineMacro("__cpp_variable_templates", "201304"); + } + if (LangOpts.SizedDeallocation) + Builder.defineMacro("__cpp_sized_deallocation", "201309"); +} + +static void InitializePredefinedMacros(const TargetInfo &TI, + const LangOptions &LangOpts, + const FrontendOptions &FEOpts, + MacroBuilder &Builder) { + // Compiler version introspection macros. + Builder.defineMacro("__llvm__"); // LLVM Backend + Builder.defineMacro("__clang__"); // Clang Frontend +#define TOSTR2(X) #X +#define TOSTR(X) TOSTR2(X) + Builder.defineMacro("__clang_major__", TOSTR(CLANG_VERSION_MAJOR)); + Builder.defineMacro("__clang_minor__", TOSTR(CLANG_VERSION_MINOR)); +#ifdef CLANG_VERSION_PATCHLEVEL + Builder.defineMacro("__clang_patchlevel__", TOSTR(CLANG_VERSION_PATCHLEVEL)); +#else + Builder.defineMacro("__clang_patchlevel__", "0"); +#endif + Builder.defineMacro("__clang_version__", + "\"" CLANG_VERSION_STRING " " + + getClangFullRepositoryVersion() + "\""); +#undef TOSTR +#undef TOSTR2 + if (!LangOpts.MSVCCompat) { + // Currently claim to be compatible with GCC 4.2.1-5621, but only if we're + // not compiling for MSVC compatibility + Builder.defineMacro("__GNUC_MINOR__", "2"); + Builder.defineMacro("__GNUC_PATCHLEVEL__", "1"); + Builder.defineMacro("__GNUC__", "4"); + Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + } + + // Define macros for the C11 / C++11 memory orderings + Builder.defineMacro("__ATOMIC_RELAXED", "0"); + Builder.defineMacro("__ATOMIC_CONSUME", "1"); + Builder.defineMacro("__ATOMIC_ACQUIRE", "2"); + Builder.defineMacro("__ATOMIC_RELEASE", "3"); + Builder.defineMacro("__ATOMIC_ACQ_REL", "4"); + Builder.defineMacro("__ATOMIC_SEQ_CST", "5"); + + // Support for #pragma redefine_extname (Sun compatibility) + Builder.defineMacro("__PRAGMA_REDEFINE_EXTNAME", "1"); + + // As sad as it is, enough software depends on the __VERSION__ for version + // checks that it is necessary to report 4.2.1 (the base GCC version we claim + // compatibility with) first. + Builder.defineMacro("__VERSION__", "\"4.2.1 Compatible " + + Twine(getClangFullCPPVersion()) + "\""); + + // Initialize language-specific preprocessor defines. + + // Standard conforming mode? + if (!LangOpts.GNUMode && !LangOpts.MSVCCompat) + Builder.defineMacro("__STRICT_ANSI__"); + + if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus11) + Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__"); + + if (LangOpts.ObjC1) { + if (LangOpts.ObjCRuntime.isNonFragile()) { + Builder.defineMacro("__OBJC2__"); + + if (LangOpts.ObjCExceptions) + Builder.defineMacro("OBJC_ZEROCOST_EXCEPTIONS"); + } + + if (LangOpts.getGC() != LangOptions::NonGC) + Builder.defineMacro("__OBJC_GC__"); + + if (LangOpts.ObjCRuntime.isNeXTFamily()) + Builder.defineMacro("__NEXT_RUNTIME__"); + + if (LangOpts.ObjCRuntime.getKind() == ObjCRuntime::ObjFW) { + VersionTuple tuple = LangOpts.ObjCRuntime.getVersion(); + + unsigned minor = 0; + if (tuple.getMinor().hasValue()) + minor = tuple.getMinor().getValue(); + + unsigned subminor = 0; + if (tuple.getSubminor().hasValue()) + subminor = tuple.getSubminor().getValue(); + + Builder.defineMacro("__OBJFW_RUNTIME_ABI__", + Twine(tuple.getMajor() * 10000 + minor * 100 + + subminor)); + } + + Builder.defineMacro("IBOutlet", "__attribute__((iboutlet))"); + Builder.defineMacro("IBOutletCollection(ClassName)", + "__attribute__((iboutletcollection(ClassName)))"); + Builder.defineMacro("IBAction", "void)__attribute__((ibaction)"); + Builder.defineMacro("IBInspectable", ""); + Builder.defineMacro("IB_DESIGNABLE", ""); + } + + if (LangOpts.CPlusPlus) + InitializeCPlusPlusFeatureTestMacros(LangOpts, Builder); + + // darwin_constant_cfstrings controls this. This is also dependent + // on other things like the runtime I believe. This is set even for C code. + if (!LangOpts.NoConstantCFStrings) + Builder.defineMacro("__CONSTANT_CFSTRINGS__"); + + if (LangOpts.ObjC2) + Builder.defineMacro("OBJC_NEW_PROPERTIES"); + + if (LangOpts.PascalStrings) + Builder.defineMacro("__PASCAL_STRINGS__"); + + if (LangOpts.Blocks) { + Builder.defineMacro("__block", "__attribute__((__blocks__(byref)))"); + Builder.defineMacro("__BLOCKS__"); + } + + if (!LangOpts.MSVCCompat && LangOpts.Exceptions) + Builder.defineMacro("__EXCEPTIONS"); + if (!LangOpts.MSVCCompat && LangOpts.RTTI) + Builder.defineMacro("__GXX_RTTI"); + if (LangOpts.SjLjExceptions) + Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); + + if (LangOpts.Deprecated) + Builder.defineMacro("__DEPRECATED"); + + if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus) { + Builder.defineMacro("__GNUG__", "4"); + Builder.defineMacro("__GXX_WEAK__"); + Builder.defineMacro("__private_extern__", "extern"); + } + + if (LangOpts.MicrosoftExt) { + if (LangOpts.WChar) { + // wchar_t supported as a keyword. + Builder.defineMacro("_WCHAR_T_DEFINED"); + Builder.defineMacro("_NATIVE_WCHAR_T_DEFINED"); + } + } + + if (LangOpts.Optimize) + Builder.defineMacro("__OPTIMIZE__"); + if (LangOpts.OptimizeSize) + Builder.defineMacro("__OPTIMIZE_SIZE__"); + + if (LangOpts.FastMath) + Builder.defineMacro("__FAST_MATH__"); + + // Initialize target-specific preprocessor defines. + + // __BYTE_ORDER__ was added in GCC 4.6. It's analogous + // to the macro __BYTE_ORDER (no trailing underscores) + // from glibc's <endian.h> header. + // We don't support the PDP-11 as a target, but include + // the define so it can still be compared against. + Builder.defineMacro("__ORDER_LITTLE_ENDIAN__", "1234"); + Builder.defineMacro("__ORDER_BIG_ENDIAN__", "4321"); + Builder.defineMacro("__ORDER_PDP_ENDIAN__", "3412"); + if (TI.isBigEndian()) { + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_BIG_ENDIAN__"); + Builder.defineMacro("__BIG_ENDIAN__"); + } else { + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"); + Builder.defineMacro("__LITTLE_ENDIAN__"); + } + + if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_LP64"); + Builder.defineMacro("__LP64__"); + } + + if (TI.getPointerWidth(0) == 32 && TI.getLongWidth() == 32 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_ILP32"); + Builder.defineMacro("__ILP32__"); + } + + // Define type sizing macros based on the target properties. + assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); + Builder.defineMacro("__CHAR_BIT__", "8"); + + DefineTypeSize("__SCHAR_MAX__", TargetInfo::SignedChar, TI, Builder); + DefineTypeSize("__SHRT_MAX__", TargetInfo::SignedShort, TI, Builder); + DefineTypeSize("__INT_MAX__", TargetInfo::SignedInt, TI, Builder); + DefineTypeSize("__LONG_MAX__", TargetInfo::SignedLong, TI, Builder); + DefineTypeSize("__LONG_LONG_MAX__", TargetInfo::SignedLongLong, TI, Builder); + DefineTypeSize("__WCHAR_MAX__", TI.getWCharType(), TI, Builder); + DefineTypeSize("__INTMAX_MAX__", TI.getIntMaxType(), TI, Builder); + DefineTypeSize("__SIZE_MAX__", TI.getSizeType(), TI, Builder); + + DefineTypeSize("__UINTMAX_MAX__", TI.getUIntMaxType(), TI, Builder); + DefineTypeSize("__PTRDIFF_MAX__", TI.getPtrDiffType(0), TI, Builder); + DefineTypeSize("__INTPTR_MAX__", TI.getIntPtrType(), TI, Builder); + DefineTypeSize("__UINTPTR_MAX__", TI.getUIntPtrType(), TI, Builder); + + DefineTypeSizeof("__SIZEOF_DOUBLE__", TI.getDoubleWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_FLOAT__", TI.getFloatWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_INT__", TI.getIntWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_LONG__", TI.getLongWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_LONG_DOUBLE__",TI.getLongDoubleWidth(),TI,Builder); + DefineTypeSizeof("__SIZEOF_LONG_LONG__", TI.getLongLongWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_POINTER__", TI.getPointerWidth(0), TI, Builder); + DefineTypeSizeof("__SIZEOF_SHORT__", TI.getShortWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_PTRDIFF_T__", + TI.getTypeWidth(TI.getPtrDiffType(0)), TI, Builder); + DefineTypeSizeof("__SIZEOF_SIZE_T__", + TI.getTypeWidth(TI.getSizeType()), TI, Builder); + DefineTypeSizeof("__SIZEOF_WCHAR_T__", + TI.getTypeWidth(TI.getWCharType()), TI, Builder); + DefineTypeSizeof("__SIZEOF_WINT_T__", + TI.getTypeWidth(TI.getWIntType()), TI, Builder); + // This is a temporary workaround while MIPS64 has not yet fully supported + // 128-bit integers. But declaration of int128 type is necessary even though + // __SIZEOF_INT128__ is undefined because c++ standard header files like + // limits throw error message if __int128 is not available. + if (TI.hasInt128Type() && !(TI.getTriple().getArch() == llvm::Triple::mips64el + || TI.getTriple().getArch() == llvm::Triple::mips64)) + DefineTypeSizeof("__SIZEOF_INT128__", 128, TI, Builder); + + DefineType("__INTMAX_TYPE__", TI.getIntMaxType(), Builder); + DefineFmt("__INTMAX", TI.getIntMaxType(), TI, Builder); + Builder.defineMacro("__INTMAX_C_SUFFIX__", + TI.getTypeConstantSuffix(TI.getIntMaxType())); + DefineType("__UINTMAX_TYPE__", TI.getUIntMaxType(), Builder); + DefineFmt("__UINTMAX", TI.getUIntMaxType(), TI, Builder); + Builder.defineMacro("__UINTMAX_C_SUFFIX__", + TI.getTypeConstantSuffix(TI.getUIntMaxType())); + DefineTypeWidth("__INTMAX_WIDTH__", TI.getIntMaxType(), TI, Builder); + DefineType("__PTRDIFF_TYPE__", TI.getPtrDiffType(0), Builder); + DefineFmt("__PTRDIFF", TI.getPtrDiffType(0), TI, Builder); + DefineTypeWidth("__PTRDIFF_WIDTH__", TI.getPtrDiffType(0), TI, Builder); + DefineType("__INTPTR_TYPE__", TI.getIntPtrType(), Builder); + DefineFmt("__INTPTR", TI.getIntPtrType(), TI, Builder); + DefineTypeWidth("__INTPTR_WIDTH__", TI.getIntPtrType(), TI, Builder); + DefineType("__SIZE_TYPE__", TI.getSizeType(), Builder); + DefineFmt("__SIZE", TI.getSizeType(), TI, Builder); + DefineTypeWidth("__SIZE_WIDTH__", TI.getSizeType(), TI, Builder); + DefineType("__WCHAR_TYPE__", TI.getWCharType(), Builder); + DefineTypeWidth("__WCHAR_WIDTH__", TI.getWCharType(), TI, Builder); + DefineType("__WINT_TYPE__", TI.getWIntType(), Builder); + DefineTypeWidth("__WINT_WIDTH__", TI.getWIntType(), TI, Builder); + DefineTypeWidth("__SIG_ATOMIC_WIDTH__", TI.getSigAtomicType(), TI, Builder); + DefineTypeSize("__SIG_ATOMIC_MAX__", TI.getSigAtomicType(), TI, Builder); + DefineType("__CHAR16_TYPE__", TI.getChar16Type(), Builder); + DefineType("__CHAR32_TYPE__", TI.getChar32Type(), Builder); + + DefineTypeWidth("__UINTMAX_WIDTH__", TI.getUIntMaxType(), TI, Builder); + DefineType("__UINTPTR_TYPE__", TI.getUIntPtrType(), Builder); + DefineFmt("__UINTPTR", TI.getUIntPtrType(), TI, Builder); + DefineTypeWidth("__UINTPTR_WIDTH__", TI.getUIntPtrType(), TI, Builder); + + DefineFloatMacros(Builder, "FLT", &TI.getFloatFormat(), "F"); + DefineFloatMacros(Builder, "DBL", &TI.getDoubleFormat(), ""); + DefineFloatMacros(Builder, "LDBL", &TI.getLongDoubleFormat(), "L"); + + // Define a __POINTER_WIDTH__ macro for stdint.h. + Builder.defineMacro("__POINTER_WIDTH__", + Twine((int)TI.getPointerWidth(0))); + + if (!LangOpts.CharIsSigned) + Builder.defineMacro("__CHAR_UNSIGNED__"); + + if (!TargetInfo::isTypeSigned(TI.getWCharType())) + Builder.defineMacro("__WCHAR_UNSIGNED__"); + + if (!TargetInfo::isTypeSigned(TI.getWIntType())) + Builder.defineMacro("__WINT_UNSIGNED__"); + + // Define exact-width integer types for stdint.h + DefineExactWidthIntType(TargetInfo::SignedChar, TI, Builder); + + if (TI.getShortWidth() > TI.getCharWidth()) + DefineExactWidthIntType(TargetInfo::SignedShort, TI, Builder); + + if (TI.getIntWidth() > TI.getShortWidth()) + DefineExactWidthIntType(TargetInfo::SignedInt, TI, Builder); + + if (TI.getLongWidth() > TI.getIntWidth()) + DefineExactWidthIntType(TargetInfo::SignedLong, TI, Builder); + + if (TI.getLongLongWidth() > TI.getLongWidth()) + DefineExactWidthIntType(TargetInfo::SignedLongLong, TI, Builder); + + DefineExactWidthIntType(TargetInfo::UnsignedChar, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedChar, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedChar, TI, Builder); + + if (TI.getShortWidth() > TI.getCharWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedShort, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedShort, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedShort, TI, Builder); + } + + if (TI.getIntWidth() > TI.getShortWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedInt, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedInt, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedInt, TI, Builder); + } + + if (TI.getLongWidth() > TI.getIntWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedLong, TI, Builder); + } + + if (TI.getLongLongWidth() > TI.getLongWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedLongLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedLongLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedLongLong, TI, Builder); + } + + DefineLeastWidthIntType(8, true, TI, Builder); + DefineLeastWidthIntType(8, false, TI, Builder); + DefineLeastWidthIntType(16, true, TI, Builder); + DefineLeastWidthIntType(16, false, TI, Builder); + DefineLeastWidthIntType(32, true, TI, Builder); + DefineLeastWidthIntType(32, false, TI, Builder); + DefineLeastWidthIntType(64, true, TI, Builder); + DefineLeastWidthIntType(64, false, TI, Builder); + + DefineFastIntType(8, true, TI, Builder); + DefineFastIntType(8, false, TI, Builder); + DefineFastIntType(16, true, TI, Builder); + DefineFastIntType(16, false, TI, Builder); + DefineFastIntType(32, true, TI, Builder); + DefineFastIntType(32, false, TI, Builder); + DefineFastIntType(64, true, TI, Builder); + DefineFastIntType(64, false, TI, Builder); + + if (const char *Prefix = TI.getUserLabelPrefix()) + Builder.defineMacro("__USER_LABEL_PREFIX__", Prefix); + + if (LangOpts.FastMath || LangOpts.FiniteMathOnly) + Builder.defineMacro("__FINITE_MATH_ONLY__", "1"); + else + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + + if (!LangOpts.MSVCCompat) { + if (LangOpts.GNUInline) + Builder.defineMacro("__GNUC_GNU_INLINE__"); + else + Builder.defineMacro("__GNUC_STDC_INLINE__"); + + // The value written by __atomic_test_and_set. + // FIXME: This is target-dependent. + Builder.defineMacro("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", "1"); + + // Used by libstdc++ to implement ATOMIC_<foo>_LOCK_FREE. + unsigned InlineWidthBits = TI.getMaxAtomicInlineWidth(); +#define DEFINE_LOCK_FREE_MACRO(TYPE, Type) \ + Builder.defineMacro("__GCC_ATOMIC_" #TYPE "_LOCK_FREE", \ + getLockFreeValue(TI.get##Type##Width(), \ + TI.get##Type##Align(), \ + InlineWidthBits)); + DEFINE_LOCK_FREE_MACRO(BOOL, Bool); + DEFINE_LOCK_FREE_MACRO(CHAR, Char); + DEFINE_LOCK_FREE_MACRO(CHAR16_T, Char16); + DEFINE_LOCK_FREE_MACRO(CHAR32_T, Char32); + DEFINE_LOCK_FREE_MACRO(WCHAR_T, WChar); + DEFINE_LOCK_FREE_MACRO(SHORT, Short); + DEFINE_LOCK_FREE_MACRO(INT, Int); + DEFINE_LOCK_FREE_MACRO(LONG, Long); + DEFINE_LOCK_FREE_MACRO(LLONG, LongLong); + Builder.defineMacro("__GCC_ATOMIC_POINTER_LOCK_FREE", + getLockFreeValue(TI.getPointerWidth(0), + TI.getPointerAlign(0), + InlineWidthBits)); +#undef DEFINE_LOCK_FREE_MACRO + } + + if (LangOpts.NoInlineDefine) + Builder.defineMacro("__NO_INLINE__"); + + if (unsigned PICLevel = LangOpts.PICLevel) { + Builder.defineMacro("__PIC__", Twine(PICLevel)); + Builder.defineMacro("__pic__", Twine(PICLevel)); + } + if (unsigned PIELevel = LangOpts.PIELevel) { + Builder.defineMacro("__PIE__", Twine(PIELevel)); + Builder.defineMacro("__pie__", Twine(PIELevel)); + } + + // Macros to control C99 numerics and <float.h> + Builder.defineMacro("__FLT_EVAL_METHOD__", Twine(TI.getFloatEvalMethod())); + Builder.defineMacro("__FLT_RADIX__", "2"); + int Dig = PickFP(&TI.getLongDoubleFormat(), -1/*FIXME*/, 17, 21, 33, 36); + Builder.defineMacro("__DECIMAL_DIG__", Twine(Dig)); + + if (LangOpts.getStackProtector() == LangOptions::SSPOn) + Builder.defineMacro("__SSP__"); + else if (LangOpts.getStackProtector() == LangOptions::SSPStrong) + Builder.defineMacro("__SSP_STRONG__", "2"); + else if (LangOpts.getStackProtector() == LangOptions::SSPReq) + Builder.defineMacro("__SSP_ALL__", "3"); + + if (FEOpts.ProgramAction == frontend::RewriteObjC) + Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); + + // Define a macro that exists only when using the static analyzer. + if (FEOpts.ProgramAction == frontend::RunAnalysis) + Builder.defineMacro("__clang_analyzer__"); + + if (LangOpts.FastRelaxedMath) + Builder.defineMacro("__FAST_RELAXED_MATH__"); + + if (LangOpts.ObjCAutoRefCount) { + Builder.defineMacro("__weak", "__attribute__((objc_ownership(weak)))"); + Builder.defineMacro("__strong", "__attribute__((objc_ownership(strong)))"); + Builder.defineMacro("__autoreleasing", + "__attribute__((objc_ownership(autoreleasing)))"); + Builder.defineMacro("__unsafe_unretained", + "__attribute__((objc_ownership(none)))"); + } + + // OpenMP definition + if (LangOpts.OpenMP) { + // OpenMP 2.2: + // In implementations that support a preprocessor, the _OPENMP + // macro name is defined to have the decimal value yyyymm where + // yyyy and mm are the year and the month designations of the + // version of the OpenMP API that the implementation support. + Builder.defineMacro("_OPENMP", "201307"); + } + + // CUDA device path compilaton + if (LangOpts.CUDAIsDevice) { + // The CUDA_ARCH value is set for the GPU target specified in the NVPTX + // backend's target defines. + Builder.defineMacro("__CUDA_ARCH__"); + } + + // Get other target #defines. + TI.getTargetDefines(LangOpts, Builder); +} + +/// InitializePreprocessor - Initialize the preprocessor getting it and the +/// environment ready to process a single file. This returns true on error. +/// +void clang::InitializePreprocessor(Preprocessor &PP, + const PreprocessorOptions &InitOpts, + const FrontendOptions &FEOpts) { + const LangOptions &LangOpts = PP.getLangOpts(); + std::string PredefineBuffer; + PredefineBuffer.reserve(4080); + llvm::raw_string_ostream Predefines(PredefineBuffer); + MacroBuilder Builder(Predefines); + + // Emit line markers for various builtin sections of the file. We don't do + // this in asm preprocessor mode, because "# 4" is not a line marker directive + // in this mode. + if (!PP.getLangOpts().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 3"); + + // Install things like __POWERPC__, __GNUC__, etc into the macro table. + if (InitOpts.UsePredefines) { + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, Builder); + + // Install definitions to make Objective-C++ ARC work well with various + // C++ Standard Library implementations. + if (LangOpts.ObjC1 && LangOpts.CPlusPlus && LangOpts.ObjCAutoRefCount) { + switch (InitOpts.ObjCXXARCStandardLibrary) { + case ARCXX_nolib: + case ARCXX_libcxx: + break; + + case ARCXX_libstdcxx: + AddObjCXXARCLibstdcxxDefines(LangOpts, Builder); + break; + } + } + } + + // Even with predefines off, some macros are still predefined. + // These should all be defined in the preprocessor according to the + // current language configuration. + InitializeStandardPredefinedMacros(PP.getTargetInfo(), PP.getLangOpts(), + FEOpts, Builder); + + // Add on the predefines from the driver. Wrap in a #line directive to report + // that they come from the command line. + if (!PP.getLangOpts().AsmPreprocessor) + Builder.append("# 1 \"<command line>\" 1"); + + // Process #define's and #undef's in the order they are given. + for (unsigned i = 0, e = InitOpts.Macros.size(); i != e; ++i) { + if (InitOpts.Macros[i].second) // isUndef + Builder.undefineMacro(InitOpts.Macros[i].first); + else + DefineBuiltinMacro(Builder, InitOpts.Macros[i].first, + PP.getDiagnostics()); + } + + // If -imacros are specified, include them now. These are processed before + // any -include directives. + for (unsigned i = 0, e = InitOpts.MacroIncludes.size(); i != e; ++i) + AddImplicitIncludeMacros(Builder, InitOpts.MacroIncludes[i]); + + // Process -include-pch/-include-pth directives. + if (!InitOpts.ImplicitPCHInclude.empty()) + AddImplicitIncludePCH(Builder, PP, InitOpts.ImplicitPCHInclude); + if (!InitOpts.ImplicitPTHInclude.empty()) + AddImplicitIncludePTH(Builder, PP, InitOpts.ImplicitPTHInclude); + + // Process -include directives. + for (unsigned i = 0, e = InitOpts.Includes.size(); i != e; ++i) { + const std::string &Path = InitOpts.Includes[i]; + AddImplicitInclude(Builder, Path); + } + + // Exit the command line and go back to <built-in> (2 is LC_LEAVE). + if (!PP.getLangOpts().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 2"); + + // Instruct the preprocessor to skip the preamble. + PP.setSkipMainFilePreamble(InitOpts.PrecompiledPreambleBytes.first, + InitOpts.PrecompiledPreambleBytes.second); + + // Copy PredefinedBuffer into the Preprocessor. + PP.setPredefines(Predefines.str()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp new file mode 100644 index 0000000..f133327 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp @@ -0,0 +1,43 @@ +//===--- LangStandards.cpp - Language Standard Definitions ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/LangStandard.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +using namespace clang; +using namespace clang::frontend; + +#define LANGSTANDARD(id, name, desc, features) \ + static const LangStandard Lang_##id = { name, desc, features }; +#include "clang/Frontend/LangStandards.def" + +const LangStandard &LangStandard::getLangStandardForKind(Kind K) { + switch (K) { + case lang_unspecified: + llvm::report_fatal_error("getLangStandardForKind() on unspecified kind"); +#define LANGSTANDARD(id, name, desc, features) \ + case lang_##id: return Lang_##id; +#include "clang/Frontend/LangStandards.def" + } + llvm_unreachable("Invalid language kind!"); +} + +const LangStandard *LangStandard::getLangStandardForName(StringRef Name) { + Kind K = llvm::StringSwitch<Kind>(Name) +#define LANGSTANDARD(id, name, desc, features) \ + .Case(name, lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(lang_unspecified); + if (K == lang_unspecified) + return nullptr; + + return &getLangStandardForKind(K); +} + + diff --git a/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp new file mode 100644 index 0000000..924a640 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp @@ -0,0 +1,208 @@ +//===--- LayoutOverrideSource.cpp --Override Record Layouts ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/CharInfo.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <string> + +using namespace clang; + +/// \brief Parse a simple identifier. +static std::string parseName(StringRef S) { + if (S.empty() || !isIdentifierHead(S[0])) + return ""; + + unsigned Offset = 1; + while (Offset < S.size() && isIdentifierBody(S[Offset])) + ++Offset; + + return S.substr(0, Offset).str(); +} + +LayoutOverrideSource::LayoutOverrideSource(StringRef Filename) { + std::ifstream Input(Filename.str().c_str()); + if (!Input.is_open()) + return; + + // Parse the output of -fdump-record-layouts. + std::string CurrentType; + Layout CurrentLayout; + bool ExpectingType = false; + + while (Input.good()) { + std::string Line; + getline(Input, Line); + + StringRef LineStr(Line); + + // Determine whether the following line will start a + if (LineStr.find("*** Dumping AST Record Layout") != StringRef::npos) { + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; + CurrentLayout = Layout(); + + ExpectingType = true; + continue; + } + + // If we're expecting a type, grab it. + if (ExpectingType) { + ExpectingType = false; + + StringRef::size_type Pos; + if ((Pos = LineStr.find("struct ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("struct ")); + else if ((Pos = LineStr.find("class ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("class ")); + else if ((Pos = LineStr.find("union ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("union ")); + else + continue; + + // Find the name of the type. + CurrentType = parseName(LineStr); + CurrentLayout = Layout(); + continue; + } + + // Check for the size of the type. + StringRef::size_type Pos = LineStr.find(" Size:"); + if (Pos != StringRef::npos) { + // Skip past the " Size:" prefix. + LineStr = LineStr.substr(Pos + strlen(" Size:")); + + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + continue; + } + + // Check for the alignment of the type. + Pos = LineStr.find("Alignment:"); + if (Pos != StringRef::npos) { + // Skip past the "Alignment:" prefix. + LineStr = LineStr.substr(Pos + strlen("Alignment:")); + + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + continue; + } + + // Check for the size/alignment of the type. + Pos = LineStr.find("sizeof="); + if (Pos != StringRef::npos) { + /* Skip past the sizeof= prefix. */ + LineStr = LineStr.substr(Pos + strlen("sizeof=")); + + // Parse size. + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + + Pos = LineStr.find("align="); + if (Pos != StringRef::npos) { + /* Skip past the align= prefix. */ + LineStr = LineStr.substr(Pos + strlen("align=")); + + // Parse alignment. + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + } + + continue; + } + + // Check for the field offsets of the type. + Pos = LineStr.find("FieldOffsets: ["); + if (Pos == StringRef::npos) + continue; + + LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); + while (!LineStr.empty() && isDigit(LineStr[0])) { + // Parse this offset. + unsigned Idx = 1; + while (Idx < LineStr.size() && isDigit(LineStr[Idx])) + ++Idx; + + unsigned long long Offset = 0; + (void)LineStr.substr(0, Idx).getAsInteger(10, Offset); + + CurrentLayout.FieldOffsets.push_back(Offset); + + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(Idx + 1); + while (!LineStr.empty() && isWhitespace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } + + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; +} + +bool +LayoutOverrideSource::layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets) +{ + // We can't override unnamed declarations. + if (!Record->getIdentifier()) + return false; + + // Check whether we have a layout for this record. + llvm::StringMap<Layout>::iterator Known = Layouts.find(Record->getName()); + if (Known == Layouts.end()) + return false; + + // Provide field layouts. + unsigned NumFields = 0; + for (RecordDecl::field_iterator F = Record->field_begin(), + FEnd = Record->field_end(); + F != FEnd; ++F, ++NumFields) { + if (NumFields >= Known->second.FieldOffsets.size()) + continue; + + FieldOffsets[*F] = Known->second.FieldOffsets[NumFields]; + } + + // Wrong number of fields. + if (NumFields != Known->second.FieldOffsets.size()) + return false; + + Size = Known->second.Size; + Alignment = Known->second.Align; + return true; +} + +void LayoutOverrideSource::dump() { + raw_ostream &OS = llvm::errs(); + for (llvm::StringMap<Layout>::iterator L = Layouts.begin(), + LEnd = Layouts.end(); + L != LEnd; ++L) { + OS << "Type: blah " << L->first() << '\n'; + OS << " Size:" << L->second.Size << '\n'; + OS << " Alignment:" << L->second.Align << '\n'; + OS << " FieldOffsets: ["; + for (unsigned I = 0, N = L->second.FieldOffsets.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << L->second.FieldOffsets[I]; + } + OS << "]\n"; + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp new file mode 100644 index 0000000..c6a18e0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -0,0 +1,165 @@ +//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/PlistSupport.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; +using namespace markup; + +LogDiagnosticPrinter::LogDiagnosticPrinter( + raw_ostream &os, DiagnosticOptions *diags, + std::unique_ptr<raw_ostream> StreamOwner) + : OS(os), StreamOwner(std::move(StreamOwner)), LangOpts(nullptr), + DiagOpts(diags) {} + +static StringRef getLevelName(DiagnosticsEngine::Level Level) { + switch (Level) { + case DiagnosticsEngine::Ignored: return "ignored"; + case DiagnosticsEngine::Remark: return "remark"; + case DiagnosticsEngine::Note: return "note"; + case DiagnosticsEngine::Warning: return "warning"; + case DiagnosticsEngine::Error: return "error"; + case DiagnosticsEngine::Fatal: return "fatal error"; + } + llvm_unreachable("Invalid DiagnosticsEngine level!"); +} + +void +LogDiagnosticPrinter::EmitDiagEntry(llvm::raw_ostream &OS, + const LogDiagnosticPrinter::DiagEntry &DE) { + OS << " <dict>\n"; + OS << " <key>level</key>\n" + << " "; + EmitString(OS, getLevelName(DE.DiagnosticLevel)) << '\n'; + if (!DE.Filename.empty()) { + OS << " <key>filename</key>\n" + << " "; + EmitString(OS, DE.Filename) << '\n'; + } + if (DE.Line != 0) { + OS << " <key>line</key>\n" + << " "; + EmitInteger(OS, DE.Line) << '\n'; + } + if (DE.Column != 0) { + OS << " <key>column</key>\n" + << " "; + EmitInteger(OS, DE.Column) << '\n'; + } + if (!DE.Message.empty()) { + OS << " <key>message</key>\n" + << " "; + EmitString(OS, DE.Message) << '\n'; + } + OS << " <key>ID</key>\n" + << " "; + EmitInteger(OS, DE.DiagnosticID) << '\n'; + if (!DE.WarningOption.empty()) { + OS << " <key>WarningOption</key>\n" + << " "; + EmitString(OS, DE.WarningOption) << '\n'; + } + OS << " </dict>\n"; +} + +void LogDiagnosticPrinter::EndSourceFile() { + // We emit all the diagnostics in EndSourceFile. However, we don't emit any + // entry if no diagnostics were present. + // + // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we + // will miss any diagnostics which are emitted after and outside the + // translation unit processing. + if (Entries.empty()) + return; + + // Write to a temporary string to ensure atomic write of diagnostic object. + SmallString<512> Msg; + llvm::raw_svector_ostream OS(Msg); + + OS << "<dict>\n"; + if (!MainFilename.empty()) { + OS << " <key>main-file</key>\n" + << " "; + EmitString(OS, MainFilename) << '\n'; + } + if (!DwarfDebugFlags.empty()) { + OS << " <key>dwarf-debug-flags</key>\n" + << " "; + EmitString(OS, DwarfDebugFlags) << '\n'; + } + OS << " <key>diagnostics</key>\n"; + OS << " <array>\n"; + for (auto &DE : Entries) + EmitDiagEntry(OS, DE); + OS << " </array>\n"; + OS << "</dict>\n"; + + this->OS << OS.str(); +} + +void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Initialize the main file name, if we haven't already fetched it. + if (MainFilename.empty() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + FileID FID = SM.getMainFileID(); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->isValid()) + MainFilename = FE->getName(); + } + } + + // Create the diag entry. + DiagEntry DE; + DE.DiagnosticID = Info.getID(); + DE.DiagnosticLevel = Level; + + DE.WarningOption = DiagnosticIDs::getWarningOptionForDiag(DE.DiagnosticID); + + // Format the message. + SmallString<100> MessageStr; + Info.FormatDiagnostic(MessageStr); + DE.Message = MessageStr.str(); + + // Set the location information. + DE.Filename = ""; + DE.Line = DE.Column = 0; + if (Info.getLocation().isValid() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Info.getLocation()); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->isValid()) + DE.Filename = FE->getName(); + } + } else { + DE.Filename = PLoc.getFilename(); + DE.Line = PLoc.getLine(); + DE.Column = PLoc.getColumn(); + } + } + + // Record the diagnostic entry. + Entries.push_back(DE); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/ModuleDependencyCollector.cpp b/contrib/llvm/tools/clang/lib/Frontend/ModuleDependencyCollector.cpp new file mode 100644 index 0000000..62865e9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ModuleDependencyCollector.cpp @@ -0,0 +1,93 @@ +//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Collect the dependencies of a set of modules. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +/// Private implementation for ModuleDependencyCollector +class ModuleDependencyListener : public ASTReaderListener { + ModuleDependencyCollector &Collector; + + std::error_code copyToRoot(StringRef Src); +public: + ModuleDependencyListener(ModuleDependencyCollector &Collector) + : Collector(Collector) {} + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { return true; } + bool visitInputFile(StringRef Filename, bool IsSystem, + bool IsOverridden) override; +}; +} + +void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(llvm::make_unique<ModuleDependencyListener>(*this)); +} + +void ModuleDependencyCollector::writeFileMap() { + if (Seen.empty()) + return; + + SmallString<256> Dest = getDest(); + llvm::sys::path::append(Dest, "vfs.yaml"); + + std::error_code EC; + llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text); + if (EC) { + setHasErrors(); + return; + } + VFSWriter.write(OS); +} + +std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { + using namespace llvm::sys; + + // We need an absolute path to append to the root. + SmallString<256> AbsoluteSrc = Src; + fs::make_absolute(AbsoluteSrc); + // Canonicalize to a native path to avoid mixed separator styles. + path::native(AbsoluteSrc); + // TODO: We probably need to handle .. as well as . in order to have valid + // input to the YAMLVFSWriter. + FileManager::removeDotPaths(AbsoluteSrc); + + // Build the destination path. + SmallString<256> Dest = Collector.getDest(); + path::append(Dest, path::relative_path(AbsoluteSrc)); + + // Copy the file into place. + if (std::error_code EC = fs::create_directories(path::parent_path(Dest), + /*IgnoreExisting=*/true)) + return EC; + if (std::error_code EC = fs::copy_file(AbsoluteSrc.str(), Dest.str())) + return EC; + // Use the absolute path under the root for the file mapping. + Collector.addFileMapping(AbsoluteSrc.str(), Dest.str()); + return std::error_code(); +} + +bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem, + bool IsOverridden) { + if (Collector.insertSeen(Filename)) + if (copyToRoot(Filename)) + Collector.setHasErrors(); + return true; +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp new file mode 100644 index 0000000..0198828 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp @@ -0,0 +1,324 @@ +//===- MultiplexConsumer.cpp - AST Consumer for PCH Generation --*- 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 MultiplexConsumer class. It also declares and defines +// MultiplexASTDeserializationListener and MultiplexASTMutationListener, which +// are implementation details of MultiplexConsumer. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/AST/ASTMutationListener.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Serialization/ASTDeserializationListener.h" + +using namespace clang; + +namespace clang { + +// This ASTDeserializationListener forwards its notifications to a set of +// child listeners. +class MultiplexASTDeserializationListener + : public ASTDeserializationListener { +public: + // Does NOT take ownership of the elements in L. + MultiplexASTDeserializationListener( + const std::vector<ASTDeserializationListener*>& L); + void ReaderInitialized(ASTReader *Reader) override; + void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II) override; + void TypeRead(serialization::TypeIdx Idx, QualType T) override; + void DeclRead(serialization::DeclID ID, const Decl *D) override; + void SelectorRead(serialization::SelectorID iD, Selector Sel) override; + void MacroDefinitionRead(serialization::PreprocessedEntityID, + MacroDefinition *MD) override; +private: + std::vector<ASTDeserializationListener*> Listeners; +}; + +MultiplexASTDeserializationListener::MultiplexASTDeserializationListener( + const std::vector<ASTDeserializationListener*>& L) + : Listeners(L) { +} + +void MultiplexASTDeserializationListener::ReaderInitialized( + ASTReader *Reader) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->ReaderInitialized(Reader); +} + +void MultiplexASTDeserializationListener::IdentifierRead( + serialization::IdentID ID, IdentifierInfo *II) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->IdentifierRead(ID, II); +} + +void MultiplexASTDeserializationListener::TypeRead( + serialization::TypeIdx Idx, QualType T) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->TypeRead(Idx, T); +} + +void MultiplexASTDeserializationListener::DeclRead( + serialization::DeclID ID, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclRead(ID, D); +} + +void MultiplexASTDeserializationListener::SelectorRead( + serialization::SelectorID ID, Selector Sel) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->SelectorRead(ID, Sel); +} + +void MultiplexASTDeserializationListener::MacroDefinitionRead( + serialization::PreprocessedEntityID ID, MacroDefinition *MD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->MacroDefinitionRead(ID, MD); +} + +// This ASTMutationListener forwards its notifications to a set of +// child listeners. +class MultiplexASTMutationListener : public ASTMutationListener { +public: + // Does NOT take ownership of the elements in L. + MultiplexASTMutationListener(ArrayRef<ASTMutationListener*> L); + void CompletedTagDefinition(const TagDecl *D) override; + void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override; + void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override; + void AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD, + const ClassTemplateSpecializationDecl *D) override; + void AddedCXXTemplateSpecialization(const VarTemplateDecl *TD, + const VarTemplateSpecializationDecl *D) override; + void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD, + const FunctionDecl *D) override; + void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override; + void CompletedImplicitDefinition(const FunctionDecl *D) override; + void StaticDataMemberInstantiated(const VarDecl *D) override; + void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD) override; + void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt) override; + void DeclarationMarkedUsed(const Decl *D) override; + void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + +private: + std::vector<ASTMutationListener*> Listeners; +}; + +MultiplexASTMutationListener::MultiplexASTMutationListener( + ArrayRef<ASTMutationListener*> L) + : Listeners(L.begin(), L.end()) { +} + +void MultiplexASTMutationListener::CompletedTagDefinition(const TagDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->CompletedTagDefinition(D); +} + +void MultiplexASTMutationListener::AddedVisibleDecl( + const DeclContext *DC, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedVisibleDecl(DC, D); +} + +void MultiplexASTMutationListener::AddedCXXImplicitMember( + const CXXRecordDecl *RD, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXImplicitMember(RD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const VarTemplateDecl *TD, const VarTemplateSpecializationDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const FunctionTemplateDecl *TD, const FunctionDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::DeducedReturnType(const FunctionDecl *FD, + QualType ReturnType) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeducedReturnType(FD, ReturnType); +} +void MultiplexASTMutationListener::CompletedImplicitDefinition( + const FunctionDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->CompletedImplicitDefinition(D); +} +void MultiplexASTMutationListener::StaticDataMemberInstantiated( + const VarDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->StaticDataMemberInstantiated(D); +} +void MultiplexASTMutationListener::AddedObjCCategoryToInterface( + const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCCategoryToInterface(CatD, IFD); +} +void MultiplexASTMutationListener::AddedObjCPropertyInClassExtension( + const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCPropertyInClassExtension(Prop, OrigProp, ClassExt); +} +void MultiplexASTMutationListener::DeclarationMarkedUsed(const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedUsed(D); +} +void MultiplexASTMutationListener::DeclarationMarkedOpenMPThreadPrivate( + const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D); +} + +} // end namespace clang + +MultiplexConsumer::MultiplexConsumer( + std::vector<std::unique_ptr<ASTConsumer>> C) + : Consumers(std::move(C)), MutationListener(), DeserializationListener() { + // Collect the mutation listeners and deserialization listeners of all + // children, and create a multiplex listener each if so. + std::vector<ASTMutationListener*> mutationListeners; + std::vector<ASTDeserializationListener*> serializationListeners; + for (auto &Consumer : Consumers) { + if (auto *mutationListener = Consumer->GetASTMutationListener()) + mutationListeners.push_back(mutationListener); + if (auto *serializationListener = Consumer->GetASTDeserializationListener()) + serializationListeners.push_back(serializationListener); + } + if (!mutationListeners.empty()) { + MutationListener = + llvm::make_unique<MultiplexASTMutationListener>(mutationListeners); + } + if (!serializationListeners.empty()) { + DeserializationListener = + llvm::make_unique<MultiplexASTDeserializationListener>( + serializationListeners); + } +} + +MultiplexConsumer::~MultiplexConsumer() {} + +void MultiplexConsumer::Initialize(ASTContext &Context) { + for (auto &Consumer : Consumers) + Consumer->Initialize(Context); +} + +bool MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { + bool Continue = true; + for (auto &Consumer : Consumers) + Continue = Continue && Consumer->HandleTopLevelDecl(D); + return Continue; +} + +void MultiplexConsumer::HandleInlineMethodDefinition(CXXMethodDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleInlineMethodDefinition(D); +} + +void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { + for (auto &Consumer : Consumers) + Consumer->HandleCXXStaticMemberVarInstantiation(VD); +} + +void MultiplexConsumer::HandleInterestingDecl(DeclGroupRef D) { + for (auto &Consumer : Consumers) + Consumer->HandleInterestingDecl(D); +} + +void MultiplexConsumer::HandleTranslationUnit(ASTContext &Ctx) { + for (auto &Consumer : Consumers) + Consumer->HandleTranslationUnit(Ctx); +} + +void MultiplexConsumer::HandleTagDeclDefinition(TagDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleTagDeclDefinition(D); +} + +void MultiplexConsumer::HandleTagDeclRequiredDefinition(const TagDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleTagDeclRequiredDefinition(D); +} + +void MultiplexConsumer::HandleCXXImplicitFunctionInstantiation(FunctionDecl *D){ + for (auto &Consumer : Consumers) + Consumer->HandleCXXImplicitFunctionInstantiation(D); +} + +void MultiplexConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (auto &Consumer : Consumers) + Consumer->HandleTopLevelDeclInObjCContainer(D); +} + +void MultiplexConsumer::HandleImplicitImportDecl(ImportDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleImplicitImportDecl(D); +} + +void MultiplexConsumer::HandleLinkerOptionPragma(llvm::StringRef Opts) { + for (auto &Consumer : Consumers) + Consumer->HandleLinkerOptionPragma(Opts); +} + +void MultiplexConsumer::HandleDetectMismatch(llvm::StringRef Name, llvm::StringRef Value) { + for (auto &Consumer : Consumers) + Consumer->HandleDetectMismatch(Name, Value); +} + +void MultiplexConsumer::HandleDependentLibrary(llvm::StringRef Lib) { + for (auto &Consumer : Consumers) + Consumer->HandleDependentLibrary(Lib); +} + +void MultiplexConsumer::CompleteTentativeDefinition(VarDecl *D) { + for (auto &Consumer : Consumers) + Consumer->CompleteTentativeDefinition(D); +} + +void MultiplexConsumer::HandleVTable( + CXXRecordDecl *RD, bool DefinitionRequired) { + for (auto &Consumer : Consumers) + Consumer->HandleVTable(RD, DefinitionRequired); +} + +ASTMutationListener *MultiplexConsumer::GetASTMutationListener() { + return MutationListener.get(); +} + +ASTDeserializationListener *MultiplexConsumer::GetASTDeserializationListener() { + return DeserializationListener.get(); +} + +void MultiplexConsumer::PrintStats() { + for (auto &Consumer : Consumers) + Consumer->PrintStats(); +} + +void MultiplexConsumer::InitializeSema(Sema &S) { + for (auto &Consumer : Consumers) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumer.get())) + SC->InitializeSema(S); +} + +void MultiplexConsumer::ForgetSema() { + for (auto &Consumer : Consumers) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumer.get())) + SC->ForgetSema(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp new file mode 100644 index 0000000..7c1d9a5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -0,0 +1,756 @@ +//===--- 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/Frontend/Utils.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/TokenConcatenation.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +using namespace clang; + +/// PrintMacroDefinition - Print a macro definition in a form that will be +/// properly accepted back as a definition. +static void PrintMacroDefinition(const IdentifierInfo &II, const MacroInfo &MI, + Preprocessor &PP, raw_ostream &OS) { + OS << "#define " << II.getName(); + + if (MI.isFunctionLike()) { + OS << '('; + if (!MI.arg_empty()) { + MacroInfo::arg_iterator AI = MI.arg_begin(), E = MI.arg_end(); + for (; AI+1 != E; ++AI) { + OS << (*AI)->getName(); + OS << ','; + } + + // Last argument. + if ((*AI)->getName() == "__VA_ARGS__") + OS << "..."; + else + OS << (*AI)->getName(); + } + + if (MI.isGNUVarargs()) + OS << "..."; // #define foo(x...) + + 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 << ' '; + + SmallString<128> SpellingBuffer; + for (MacroInfo::tokens_iterator I = MI.tokens_begin(), E = MI.tokens_end(); + I != E; ++I) { + if (I->hasLeadingSpace()) + OS << ' '; + + OS << PP.getSpelling(*I, SpellingBuffer); + } +} + +//===----------------------------------------------------------------------===// +// Preprocessed token printer +//===----------------------------------------------------------------------===// + +namespace { +class PrintPPOutputPPCallbacks : public PPCallbacks { + Preprocessor &PP; + SourceManager &SM; + TokenConcatenation ConcatInfo; +public: + raw_ostream &OS; +private: + unsigned CurLine; + + bool EmittedTokensOnThisLine; + bool EmittedDirectiveOnThisLine; + SrcMgr::CharacteristicKind FileType; + SmallString<512> CurFilename; + bool Initialized; + bool DisableLineMarkers; + bool DumpDefines; + bool UseLineDirective; + bool IsFirstFileEntered; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, + bool lineMarkers, bool defines) + : PP(pp), SM(PP.getSourceManager()), + ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), + DumpDefines(defines) { + CurLine = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + IsFirstFileEntered = false; + + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; + } + + void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; } + bool hasEmittedDirectiveOnThisLine() const { + return EmittedDirectiveOnThisLine; + } + + bool startNewLineIfNeeded(bool ShouldUpdateCurrentLine = true); + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override; + void Ident(SourceLocation Loc, const std::string &str) override; + void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) override; + void PragmaDebug(SourceLocation Loc, StringRef DebugType) override; + void PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) override; + void PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) override; + void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Severity Map, StringRef Str) override; + void PragmaWarning(SourceLocation Loc, StringRef WarningSpec, + ArrayRef<int> Ids) override; + void PragmaWarningPush(SourceLocation Loc, int Level) override; + void PragmaWarningPop(SourceLocation Loc) override; + + bool HandleFirstTokOnLine(Token &Tok); + + /// Move to the line of the provided source location. This will + /// return true if the output stream required adjustment or if + /// the requested location is on the first line. + bool MoveToLine(SourceLocation Loc) { + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + if (PLoc.isInvalid()) + return false; + return MoveToLine(PLoc.getLine()) || (PLoc.getLine() == 1); + } + bool MoveToLine(unsigned LineNo); + + bool AvoidConcat(const Token &PrevPrevTok, const Token &PrevTok, + const Token &Tok) { + return ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok); + } + void WriteLineInfo(unsigned LineNo, const char *Extra=nullptr, + unsigned ExtraLen=0); + bool LineMarkersAreDisabled() const { return DisableLineMarkers; } + void HandleNewlinesInToken(const char *TokStr, unsigned Len); + + /// MacroDefined - This hook is called whenever a macro definition is seen. + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override; + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + void MacroUndefined(const Token &MacroNameTok, + const MacroDirective *MD) override; +}; +} // end anonymous namespace + +void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo, + const char *Extra, + unsigned ExtraLen) { + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + + // Emit #line directives or GNU line markers depending on what mode we're in. + if (UseLineDirective) { + OS << "#line" << ' ' << LineNo << ' ' << '"'; + OS.write_escaped(CurFilename); + OS << '"'; + } else { + OS << '#' << ' ' << LineNo << ' ' << '"'; + OS.write_escaped(CurFilename); + 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(unsigned LineNo) { + // 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 expansion line didn't. + else { + const char *NewLines = "\n\n\n\n\n\n\n\n"; + OS.write(NewLines, LineNo-CurLine); + } + } else if (!DisableLineMarkers) { + // Emit a #line or line marker. + WriteLineInfo(LineNo, nullptr, 0); + } else { + // Okay, we're in -P mode, which turns off line markers. However, we still + // need to emit a newline between tokens on different lines. + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + } + + CurLine = LineNo; + return true; +} + +bool +PrintPPOutputPPCallbacks::startNewLineIfNeeded(bool ShouldUpdateCurrentLine) { + if (EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + if (ShouldUpdateCurrentLine) + ++CurLine; + return true; + } + + return false; +} + +/// 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, + FileID PrevFID) { + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + SourceManager &SourceMgr = SM; + + PresumedLoc UserLoc = SourceMgr.getPresumedLoc(Loc); + if (UserLoc.isInvalid()) + return; + + unsigned NewLine = UserLoc.getLine(); + + if (Reason == PPCallbacks::EnterFile) { + SourceLocation IncludeLoc = UserLoc.getIncludeLoc(); + if (IncludeLoc.isValid()) + MoveToLine(IncludeLoc); + } else if (Reason == PPCallbacks::SystemHeaderPragma) { + // GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. This is because + // otherwise we will emit a line marker for THIS line, which requires an + // extra blank line after the directive to avoid making all following lines + // off by one. We can do better by simply incrementing NewLine here. + NewLine += 1; + } + + CurLine = NewLine; + + CurFilename.clear(); + CurFilename += UserLoc.getFilename(); + FileType = NewFileType; + + if (DisableLineMarkers) { + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + return; + } + + if (!Initialized) { + WriteLineInfo(CurLine); + Initialized = true; + } + + // Do not emit an enter marker for the main file (which we expect is the first + // entered file). This matches gcc, and improves compatibility with some tools + // which track the # line markers as a way to determine when the preprocessed + // output is in the context of the main file. + if (Reason == PPCallbacks::EnterFile && !IsFirstFileEntered) { + IsFirstFileEntered = true; + return; + } + + 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; + } +} + +void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + // When preprocessing, turn implicit imports into @imports. + // FIXME: This is a stop-gap until a more comprehensive "preprocessing with + // modules" solution is introduced. + if (Imported) { + startNewLineIfNeeded(); + MoveToLine(HashLoc); + OS << "@import " << Imported->getFullModuleName() << ";" + << " /* clang -E: implicit import for \"" << File->getName() << "\" */"; + // Since we want a newline after the @import, but not a #<line>, start a new + // line immediately. + EmittedTokensOnThisLine = true; + startNewLineIfNeeded(); + } +} + +/// 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; +} + +/// MacroDefined - This hook is called whenever a macro definition is seen. +void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + const MacroInfo *MI = MD->getMacroInfo(); + // Only print out macro definitions in -dD mode. + if (!DumpDefines || + // Ignore __FILE__ etc. + MI->isBuiltinMacro()) return; + + MoveToLine(MI->getDefinitionLoc()); + PrintMacroDefinition(*MacroNameTok.getIdentifierInfo(), *MI, PP, OS); + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, + const MacroDirective *MD) { + // Only print out macro definitions in -dD mode. + if (!DumpDefines) return; + + MoveToLine(MacroNameTok.getLocation()); + OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName(); + setEmittedDirectiveOnThisLine(); +} + +static void outputPrintable(llvm::raw_ostream& OS, + const std::string &Str) { + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char Char = Str[i]; + if (isPrintable(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)); + } +} + +void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, + StringRef Namespace, + PragmaMessageKind Kind, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma "; + if (!Namespace.empty()) + OS << Namespace << ' '; + switch (Kind) { + case PMK_Message: + OS << "message(\""; + break; + case PMK_Warning: + OS << "warning \""; + break; + case PMK_Error: + OS << "error \""; + break; + } + + outputPrintable(OS, Str); + OS << '"'; + if (Kind == PMK_Message) + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDebug(SourceLocation Loc, + StringRef DebugType) { + startNewLineIfNeeded(); + MoveToLine(Loc); + + OS << "#pragma clang __debug "; + OS << DebugType; + + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic push"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic pop"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDiagnostic(SourceLocation Loc, + StringRef Namespace, + diag::Severity Map, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic "; + switch (Map) { + case diag::Severity::Remark: + OS << "remark"; + break; + case diag::Severity::Warning: + OS << "warning"; + break; + case diag::Severity::Error: + OS << "error"; + break; + case diag::Severity::Ignored: + OS << "ignored"; + break; + case diag::Severity::Fatal: + OS << "fatal"; + break; + } + OS << " \"" << Str << '"'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarning(SourceLocation Loc, + StringRef WarningSpec, + ArrayRef<int> Ids) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(" << WarningSpec << ':'; + for (ArrayRef<int>::iterator I = Ids.begin(), E = Ids.end(); I != E; ++I) + OS << ' ' << *I; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPush(SourceLocation Loc, + int Level) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(push"; + if (Level >= 0) + OS << ", " << Level; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPop(SourceLocation Loc) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(pop)"; + setEmittedDirectiveOnThisLine(); +} + +/// 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 expansion 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. + unsigned ColNo = SM.getExpansionColumnNumber(Tok.getLocation()); + + // The first token on a line can have a column number of 1, yet still expect + // leading white space, if a macro expansion in column 1 starts with an empty + // macro argument, or an empty nested macro expansion. In this case, move the + // token to column 2. + if (ColNo == 1 && Tok.hasLeadingSpace()) + ColNo = 2; + + // 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; +} + +void PrintPPOutputPPCallbacks::HandleNewlinesInToken(const char *TokStr, + unsigned Len) { + unsigned NumNewlines = 0; + for (; Len; --Len, ++TokStr) { + if (*TokStr != '\n' && + *TokStr != '\r') + continue; + + ++NumNewlines; + + // If we have \n\r or \r\n, skip both and count as one line. + if (Len != 1 && + (TokStr[1] == '\n' || TokStr[1] == '\r') && + TokStr[0] != TokStr[1]) + ++TokStr, --Len; + } + + if (NumNewlines == 0) return; + + CurLine += NumNewlines; +} + + +namespace { +struct UnknownPragmaHandler : public PragmaHandler { + const char *Prefix; + PrintPPOutputPPCallbacks *Callbacks; + + UnknownPragmaHandler(const char *prefix, PrintPPOutputPPCallbacks *callbacks) + : Prefix(prefix), Callbacks(callbacks) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &PragmaTok) override { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->startNewLineIfNeeded(); + Callbacks->MoveToLine(PragmaTok.getLocation()); + Callbacks->OS.write(Prefix, strlen(Prefix)); + // Read and print all of the pragma tokens. + while (PragmaTok.isNot(tok::eod)) { + if (PragmaTok.hasLeadingSpace()) + Callbacks->OS << ' '; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + + // Expand macros in pragmas with -fms-extensions. The assumption is that + // the majority of pragmas in such a file will be Microsoft pragmas. + if (PP.getLangOpts().MicrosoftExt) + PP.Lex(PragmaTok); + else + PP.LexUnexpandedToken(PragmaTok); + } + Callbacks->setEmittedDirectiveOnThisLine(); + } +}; +} // end anonymous namespace + + +static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, + PrintPPOutputPPCallbacks *Callbacks, + raw_ostream &OS) { + bool DropComments = PP.getLangOpts().TraditionalCPP && + !PP.getCommentRetentionState(); + + char Buffer[256]; + Token PrevPrevTok, PrevTok; + PrevPrevTok.startToken(); + PrevTok.startToken(); + while (1) { + if (Callbacks->hasEmittedDirectiveOnThisLine()) { + Callbacks->startNewLineIfNeeded(); + Callbacks->MoveToLine(Tok.getLocation()); + } + + // 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(PrevPrevTok, PrevTok, Tok))) { + OS << ' '; + } + + if (DropComments && Tok.is(tok::comment)) { + // Skip comments. Normally the preprocessor does not generate + // tok::comment nodes at all when not keeping comments, but under + // -traditional-cpp the lexer keeps /all/ whitespace, including comments. + SourceLocation StartLoc = Tok.getLocation(); + Callbacks->MoveToLine(StartLoc.getLocWithOffset(Tok.getLength())); + } else if (Tok.is(tok::annot_module_include) || + Tok.is(tok::annot_module_begin) || + Tok.is(tok::annot_module_end)) { + // PrintPPOutputPPCallbacks::InclusionDirective handles producing + // appropriate output here. Ignore this token entirely. + PP.Lex(Tok); + continue; + } else if (IdentifierInfo *II = Tok.getIdentifierInfo()) { + OS << II->getName(); + } 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); + + // Tokens that can contain embedded newlines need to adjust our current + // line number. + if (Tok.getKind() == tok::comment || Tok.getKind() == tok::unknown) + Callbacks->HandleNewlinesInToken(TokPtr, Len); + } else { + std::string S = PP.getSpelling(Tok); + OS.write(&S[0], S.size()); + + // Tokens that can contain embedded newlines need to adjust our current + // line number. + if (Tok.getKind() == tok::comment || Tok.getKind() == tok::unknown) + Callbacks->HandleNewlinesInToken(&S[0], S.size()); + } + Callbacks->setEmittedTokensOnThisLine(); + + if (Tok.is(tok::eof)) break; + + PrevPrevTok = PrevTok; + PrevTok = Tok; + PP.Lex(Tok); + } +} + +typedef std::pair<const IdentifierInfo *, MacroInfo *> id_macro_pair; +static int MacroIDCompare(const id_macro_pair *LHS, const id_macro_pair *RHS) { + return LHS->first->getName().compare(RHS->first->getName()); +} + +static void DoPrintMacros(Preprocessor &PP, raw_ostream *OS) { + // Ignore unknown pragmas. + PP.IgnorePragmas(); + + // -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)); + + SmallVector<id_macro_pair, 128> MacrosByID; + for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); + I != E; ++I) { + if (I->first->hasMacroDefinition()) + MacrosByID.push_back(id_macro_pair(I->first, I->second->getMacroInfo())); + } + llvm::array_pod_sort(MacrosByID.begin(), MacrosByID.end(), MacroIDCompare); + + for (unsigned i = 0, e = MacrosByID.size(); i != e; ++i) { + MacroInfo &MI = *MacrosByID[i].second; + // Ignore computed macros like __LINE__ and friends. + if (MI.isBuiltinMacro()) continue; + + PrintMacroDefinition(*MacrosByID[i].first, MI, PP, *OS); + *OS << '\n'; + } +} + +/// DoPrintPreprocessedInput - This implements -E mode. +/// +void clang::DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + // Show macros with no output is handled specially. + if (!Opts.ShowCPP) { + assert(Opts.ShowMacros && "Not yet implemented!"); + DoPrintMacros(PP, OS); + return; + } + + // Inform the preprocessor whether we want it to retain comments or not, due + // to -C or -CC. + PP.SetCommentRetentionState(Opts.ShowComments, Opts.ShowMacroComments); + + PrintPPOutputPPCallbacks *Callbacks = + new PrintPPOutputPPCallbacks(PP, *OS, !Opts.ShowLineMarkers, + Opts.ShowMacros); + PP.AddPragmaHandler(new UnknownPragmaHandler("#pragma", Callbacks)); + PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC",Callbacks)); + PP.AddPragmaHandler("clang", + new UnknownPragmaHandler("#pragma clang", Callbacks)); + + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(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); + if (Tok.is(tok::eof) || !Tok.getLocation().isFileID()) + break; + + PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation()); + if (PLoc.isInvalid()) + break; + + if (strcmp(PLoc.getFilename(), "<built-in>")) + break; + } while (true); + + // Read all the preprocessed tokens, printing them out to the stream. + PrintPreprocessedTokens(PP, Tok, Callbacks, *OS); + *OS << '\n'; +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp new file mode 100644 index 0000000..a3e14f9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp @@ -0,0 +1,196 @@ +//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a diagnostic client adaptor that performs rewrites as +// suggested by code modification hints attached to diagnostics. It +// then forwards any diagnostics to the adapted diagnostic client. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <memory> + +using namespace clang; + +FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + const LangOptions &LangOpts, + FixItOptions *FixItOpts) + : Diags(Diags), + Editor(SourceMgr, LangOpts), + Rewrite(SourceMgr, LangOpts), + FixItOpts(FixItOpts), + NumFailures(0), + PrevDiagSilenced(false) { + Owner = Diags.takeClient(); + Client = Diags.getClient(); + Diags.setClient(this, false); +} + +FixItRewriter::~FixItRewriter() { + Diags.setClient(Client, Owner.release() != nullptr); +} + +bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { + const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); + if (!RewriteBuf) return true; + RewriteBuf->write(OS); + OS.flush(); + return false; +} + +namespace { + +class RewritesReceiver : public edit::EditsReceiver { + Rewriter &Rewrite; + +public: + RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } + + void insert(SourceLocation loc, StringRef text) override { + Rewrite.InsertText(loc, text); + } + void replace(CharSourceRange range, StringRef text) override { + Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); + } +}; + +} + +bool FixItRewriter::WriteFixedFiles( + std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { + if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { + Diag(FullSourceLoc(), diag::warn_fixit_no_changes); + return true; + } + + RewritesReceiver Rec(Rewrite); + Editor.applyRewrites(Rec); + + for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { + const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); + int fd; + std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); + std::error_code EC; + std::unique_ptr<llvm::raw_fd_ostream> OS; + if (fd != -1) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + } else { + OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None)); + } + if (EC) { + Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename + << EC.message(); + continue; + } + RewriteBuffer &RewriteBuf = I->second; + RewriteBuf.write(*OS); + OS->flush(); + + if (RewrittenFiles) + RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); + } + + return false; +} + +bool FixItRewriter::IncludeInDiagnosticCounts() const { + return Client ? Client->IncludeInDiagnosticCounts() : true; +} + +void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + + if (!FixItOpts->Silent || + DiagLevel >= DiagnosticsEngine::Error || + (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || + (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { + Client->HandleDiagnostic(DiagLevel, Info); + PrevDiagSilenced = false; + } else { + PrevDiagSilenced = true; + } + + // Skip over any diagnostics that are ignored or notes. + if (DiagLevel <= DiagnosticsEngine::Note) + return; + // Skip over errors if we are only fixing warnings. + if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { + ++NumFailures; + return; + } + + // Make sure that we can perform all of the modifications we + // in this diagnostic. + edit::Commit commit(Editor); + for (unsigned Idx = 0, Last = Info.getNumFixItHints(); + Idx < Last; ++Idx) { + const FixItHint &Hint = Info.getFixItHint(Idx); + + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); + + if (!CanRewrite) { + if (Info.getNumFixItHints() > 0) + Diag(Info.getLocation(), diag::note_fixit_in_macro); + + // If this was an error, refuse to perform any rewriting. + if (DiagLevel >= DiagnosticsEngine::Error) { + if (++NumFailures == 1) + Diag(Info.getLocation(), diag::note_fixit_unfixed_error); + } + return; + } + + if (!Editor.commit(commit)) { + ++NumFailures; + Diag(Info.getLocation(), diag::note_fixit_failed); + return; + } + + Diag(Info.getLocation(), diag::note_fixit_applied); +} + +/// \brief Emit a diagnostic via the adapted diagnostic client. +void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { + // When producing this diagnostic, we temporarily bypass ourselves, + // clear out any current diagnostic, and let the downstream client + // format the diagnostic. + Diags.setClient(Client, false); + Diags.Clear(); + Diags.Report(Loc, DiagID); + Diags.setClient(this, false); +} + +FixItOptions::~FixItOptions() {} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp new file mode 100644 index 0000000..1b5eb28 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -0,0 +1,196 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) + return CreateHTMLPrinter(OS, CI.getPreprocessor()); + return nullptr; +} + +FixItAction::FixItAction() {} +FixItAction::~FixItAction() {} + +std::unique_ptr<ASTConsumer> +FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +namespace { +class FixItRewriteInPlace : public FixItOptions { +public: + std::string RewriteFilename(const std::string &Filename, int &fd) override { + fd = -1; + return Filename; + } +}; + +class FixItActionSuffixInserter : public FixItOptions { + std::string NewSuffix; + +public: + FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) + : NewSuffix(NewSuffix) { + this->FixWhatYouCan = FixWhatYouCan; + } + + std::string RewriteFilename(const std::string &Filename, int &fd) override { + fd = -1; + SmallString<128> Path(Filename); + llvm::sys::path::replace_extension(Path, + NewSuffix + llvm::sys::path::extension(Path)); + return Path.str(); + } +}; + +class FixItRewriteToTemp : public FixItOptions { +public: + std::string RewriteFilename(const std::string &Filename, int &fd) override { + SmallString<128> Path; + llvm::sys::fs::createTemporaryFile(llvm::sys::path::filename(Filename), + llvm::sys::path::extension(Filename), fd, + Path); + return Path.str(); + } +}; +} // end anonymous namespace + +bool FixItAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); + if (!FEOpts.FixItSuffix.empty()) { + FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, + FEOpts.FixWhatYouCan)); + } else { + FixItOpts.reset(new FixItRewriteInPlace); + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + } + Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get())); + return true; +} + +void FixItAction::EndSourceFileAction() { + // Otherwise rewrite all files. + Rewriter->WriteFixedFiles(); +} + +bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { + + std::vector<std::pair<std::string, std::string> > RewrittenFiles; + bool err = false; + { + const FrontendOptions &FEOpts = CI.getFrontendOpts(); + std::unique_ptr<FrontendAction> FixAction(new SyntaxOnlyAction()); + if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { + std::unique_ptr<FixItOptions> FixItOpts; + if (FEOpts.FixToTemporaries) + FixItOpts.reset(new FixItRewriteToTemp()); + else + FixItOpts.reset(new FixItRewriteInPlace()); + FixItOpts->Silent = true; + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; + FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get()); + FixAction->Execute(); + + err = Rewriter.WriteFixedFiles(&RewrittenFiles); + + FixAction->EndSourceFile(); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + } else { + err = true; + } + } + if (err) + return false; + CI.getDiagnosticClient().clear(); + CI.getDiagnostics().Reset(); + + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(), + RewrittenFiles.begin(), RewrittenFiles.end()); + PPOpts.RemappedFilesKeepOriginalName = false; + + return true; +} + +#ifdef CLANG_ENABLE_OBJC_REWRITER + +std::unique_ptr<ASTConsumer> +RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { + if (CI.getLangOpts().ObjCRuntime.isNonFragile()) + return CreateModernObjCRewriter(InFile, OS, + CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros, + (CI.getCodeGenOpts().getDebugInfo() != + CodeGenOptions::NoDebugInfo)); + return CreateObjCRewriter(InFile, OS, + CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros); + } + return nullptr; +} + +#endif + +//===----------------------------------------------------------------------===// +// Preprocessor Actions +//===----------------------------------------------------------------------===// + +void RewriteMacrosAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + RewriteMacrosInInput(CI.getPreprocessor(), OS); +} + +void RewriteTestAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(false, getCurrentFile()); + if (!OS) return; + + DoRewriteTest(CI.getPreprocessor(), OS); +} + +void RewriteIncludesAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + RewriteIncludesInInput(CI.getPreprocessor(), OS, + CI.getPreprocessorOutputOpts()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp new file mode 100644 index 0000000..22ccfe6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp @@ -0,0 +1,95 @@ +//===--- 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 "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// Functional HTML pretty-printing. +//===----------------------------------------------------------------------===// + +namespace { + class HTMLPrinter : public ASTConsumer { + Rewriter R; + raw_ostream *Out; + Preprocessor &PP; + bool SyntaxHighlight, HighlightMacros; + + public: + HTMLPrinter(raw_ostream *OS, Preprocessor &pp, + bool _SyntaxHighlight, bool _HighlightMacros) + : Out(OS), PP(pp), SyntaxHighlight(_SyntaxHighlight), + HighlightMacros(_HighlightMacros) {} + + void Initialize(ASTContext &context) override; + void HandleTranslationUnit(ASTContext &Ctx) override; + }; +} + +std::unique_ptr<ASTConsumer> clang::CreateHTMLPrinter(raw_ostream *OS, + Preprocessor &PP, + bool SyntaxHighlight, + bool HighlightMacros) { + return llvm::make_unique<HTMLPrinter>(OS, PP, SyntaxHighlight, + HighlightMacros); +} + +void HTMLPrinter::Initialize(ASTContext &context) { + R.setSourceMgr(context.getSourceManager(), context.getLangOpts()); +} + +void HTMLPrinter::HandleTranslationUnit(ASTContext &Ctx) { + if (PP.getDiagnostics().hasErrorOccurred()) + return; + + // Format the file. + FileID FID = R.getSourceMgr().getMainFileID(); + const FileEntry* Entry = R.getSourceMgr().getFileEntryForID(FID); + const char* Name; + // In some cases, in particular the case where the input is from stdin, + // there is no entry. Fall back to the memory buffer for a name in those + // cases. + if (Entry) + Name = Entry->getName(); + else + Name = R.getSourceMgr().getBuffer(FID)->getBufferIdentifier(); + + html::AddLineNumbers(R, FID); + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Name); + + // 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 (SyntaxHighlight) html::SyntaxHighlight(R, FID, PP); + if (HighlightMacros) html::HighlightMacros(R, FID, PP); + html::EscapeText(R, FID, false, true); + + // Emit the HTML. + const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID); + char *Buffer = (char*)malloc(RewriteBuf.size()); + std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer); + Out->write(Buffer, RewriteBuf.size()); + free(Buffer); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp new file mode 100644 index 0000000..1400557 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -0,0 +1,588 @@ +//===--- InclusionRewriter.cpp - Rewrite includes 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 include invocations into their expansions. This gives you +// a file with all included files merged into it. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace llvm; + +namespace { + +class InclusionRewriter : public PPCallbacks { + /// Information about which #includes were actually performed, + /// created by preprocessor callbacks. + struct FileChange { + const Module *Mod; + SourceLocation From; + FileID Id; + SrcMgr::CharacteristicKind FileType; + FileChange(SourceLocation From, const Module *Mod) : Mod(Mod), From(From) { + } + }; + Preprocessor &PP; ///< Used to find inclusion directives. + SourceManager &SM; ///< Used to read and manage source files. + raw_ostream &OS; ///< The destination stream for rewritten contents. + StringRef MainEOL; ///< The line ending marker to use. + const llvm::MemoryBuffer *PredefinesBuffer; ///< The preprocessor predefines. + bool ShowLineMarkers; ///< Show #line markers. + bool UseLineDirective; ///< Use of line directives or line markers. + typedef std::map<unsigned, FileChange> FileChangeMap; + FileChangeMap FileChanges; ///< Tracks which files were included where. + /// Used transitively for building up the FileChanges mapping over the + /// various \c PPCallbacks callbacks. + FileChangeMap::iterator LastInsertedFileChange; +public: + InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers); + bool Process(FileID FileId, SrcMgr::CharacteristicKind FileType); + void setPredefinesBuffer(const llvm::MemoryBuffer *Buf) { + PredefinesBuffer = Buf; + } + void detectMainFileEOL(); +private: + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void FileSkipped(const FileEntry &ParentFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override; + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) override; + void WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef Extra = StringRef()); + void WriteImplicitModuleImport(const Module *Mod); + void OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &lines, + bool EnsureNewline); + void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, + const MemoryBuffer &FromFile, StringRef EOL, + unsigned &NextToWrite, int &Lines); + bool HandleHasInclude(FileID FileId, Lexer &RawLex, + const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists); + const FileChange *FindFileChangeLocation(SourceLocation Loc) const; + StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); +}; + +} // end anonymous namespace + +/// Initializes an InclusionRewriter with a \p PP source and \p OS destination. +InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS, + bool ShowLineMarkers) + : PP(PP), SM(PP.getSourceManager()), OS(OS), MainEOL("\n"), + PredefinesBuffer(nullptr), ShowLineMarkers(ShowLineMarkers), + LastInsertedFileChange(FileChanges.end()) { + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; +} + +/// Write appropriate line information as either #line directives or GNU line +/// markers depending on what mode we're in, including the \p Filename and +/// \p Line we are located at, using the specified \p EOL line separator, and +/// any \p Extra context specifiers in GNU line directives. +void InclusionRewriter::WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef Extra) { + if (!ShowLineMarkers) + return; + if (UseLineDirective) { + OS << "#line" << ' ' << Line << ' ' << '"'; + OS.write_escaped(Filename); + OS << '"'; + } else { + // Use GNU linemarkers as described here: + // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html + OS << '#' << ' ' << Line << ' ' << '"'; + OS.write_escaped(Filename); + OS << '"'; + if (!Extra.empty()) + OS << Extra; + if (FileType == SrcMgr::C_System) + // "`3' This indicates that the following text comes from a system header + // file, so certain warnings should be suppressed." + OS << " 3"; + else if (FileType == SrcMgr::C_ExternCSystem) + // as above for `3', plus "`4' This indicates that the following text + // should be treated as being wrapped in an implicit extern "C" block." + OS << " 3 4"; + } + OS << MainEOL; +} + +void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod) { + OS << "@import " << Mod->getFullModuleName() << ";" + << " /* clang -frewrite-includes: implicit import */" << MainEOL; +} + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. +void InclusionRewriter::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID) { + if (Reason != EnterFile) + return; + if (LastInsertedFileChange == FileChanges.end()) + // we didn't reach this file (eg: the main file) via an inclusion directive + return; + LastInsertedFileChange->second.Id = FullSourceLoc(Loc, SM).getFileID(); + LastInsertedFileChange->second.FileType = NewFileType; + LastInsertedFileChange = FileChanges.end(); +} + +/// Called whenever an inclusion is skipped due to canonical header protection +/// macros. +void InclusionRewriter::FileSkipped(const FileEntry &/*ParentFile*/, + const Token &/*FilenameTok*/, + SrcMgr::CharacteristicKind /*FileType*/) { + assert(LastInsertedFileChange != FileChanges.end() && "A file, that wasn't " + "found via an inclusion directive, was skipped"); + FileChanges.erase(LastInsertedFileChange); + LastInsertedFileChange = FileChanges.end(); +} + +/// This should be called whenever the preprocessor encounters include +/// directives. It does not say whether the file has been included, but it +/// provides more information about the directive (hash location instead +/// of location inside the included file). It is assumed that the matching +/// FileChanged() or FileSkipped() is called after this. +void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, + const Token &/*IncludeTok*/, + StringRef /*FileName*/, + bool /*IsAngled*/, + CharSourceRange /*FilenameRange*/, + const FileEntry * /*File*/, + StringRef /*SearchPath*/, + StringRef /*RelativePath*/, + const Module *Imported) { + assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion " + "directive was found before the previous one was processed"); + std::pair<FileChangeMap::iterator, bool> p = FileChanges.insert( + std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc, Imported))); + assert(p.second && "Unexpected revisitation of the same include directive"); + if (!Imported) + LastInsertedFileChange = p.first; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of inclusion information, FileChanges. +const InclusionRewriter::FileChange * +InclusionRewriter::FindFileChangeLocation(SourceLocation Loc) const { + FileChangeMap::const_iterator I = FileChanges.find(Loc.getRawEncoding()); + if (I != FileChanges.end()) + return &I->second; + return nullptr; +} + +/// Detect the likely line ending style of \p FromFile by examining the first +/// newline found within it. +static StringRef DetectEOL(const MemoryBuffer &FromFile) { + // Detect what line endings the file uses, so that added content does not mix + // the style. We need to check for "\r\n" first because "\n\r" will match + // "\r\n\r\n". + const char *Pos = strchr(FromFile.getBufferStart(), '\n'); + if (!Pos) + return "\n"; + if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r') + return "\r\n"; + if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r') + return "\n\r"; + return "\n"; +} + +void InclusionRewriter::detectMainFileEOL() { + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(SM.getMainFileID(), &Invalid); + assert(!Invalid); + if (Invalid) + return; // Should never happen, but whatever. + MainEOL = DetectEOL(FromFile); +} + +/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at +/// \p WriteTo - 1. +void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef LocalEOL, int &Line, + bool EnsureNewline) { + if (WriteTo <= WriteFrom) + return; + if (&FromFile == PredefinesBuffer) { + // Ignore the #defines of the predefines buffer. + WriteFrom = WriteTo; + return; + } + + // If we would output half of a line ending, advance one character to output + // the whole line ending. All buffers are null terminated, so looking ahead + // one byte is safe. + if (LocalEOL.size() == 2 && + LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] && + LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0]) + WriteTo++; + + StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom, + WriteTo - WriteFrom); + + if (MainEOL == LocalEOL) { + OS << TextToWrite; + // count lines manually, it's faster than getPresumedLoc() + Line += TextToWrite.count(LocalEOL); + if (EnsureNewline && !TextToWrite.endswith(LocalEOL)) + OS << MainEOL; + } else { + // Output the file one line at a time, rewriting the line endings as we go. + StringRef Rest = TextToWrite; + while (!Rest.empty()) { + StringRef LineText; + std::tie(LineText, Rest) = Rest.split(LocalEOL); + OS << LineText; + Line++; + if (!Rest.empty()) + OS << MainEOL; + } + if (TextToWrite.endswith(LocalEOL) || EnsureNewline) + OS << MainEOL; + } + WriteFrom = WriteTo; +} + +/// Print characters from \p FromFile starting at \p NextToWrite up until the +/// inclusion directive at \p StartToken, then print out the inclusion +/// inclusion directive disabled by a #if directive, updating \p NextToWrite +/// and \p Line to track the number of source lines visited and the progress +/// through the \p FromFile buffer. +void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex, + const Token &StartToken, + const MemoryBuffer &FromFile, + StringRef LocalEOL, + unsigned &NextToWrite, int &Line) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(StartToken.getLocation()), LocalEOL, Line, + false); + Token DirectiveToken; + do { + DirectiveLex.LexFromRawLexer(DirectiveToken); + } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof)); + if (&FromFile == PredefinesBuffer) { + // OutputContentUpTo() would not output anything anyway. + return; + } + OS << "#if 0 /* expanded by -frewrite-includes */" << MainEOL; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(DirectiveToken.getLocation()) + + DirectiveToken.getLength(), + LocalEOL, Line, true); + OS << "#endif /* expanded by -frewrite-includes */" << MainEOL; +} + +/// Find the next identifier in the pragma directive specified by \p RawToken. +StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, + Token &RawToken) { + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) + return RawToken.getIdentifierInfo()->getName(); + return StringRef(); +} + +// Expand __has_include and __has_include_next if possible. If there's no +// definitive answer return false. +bool InclusionRewriter::HandleHasInclude( + FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists) { + // Lex the opening paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::l_paren)) + return false; + + RawLex.LexFromRawLexer(Tok); + + SmallString<128> FilenameBuffer; + StringRef Filename; + // Since the raw lexer doesn't give us angle_literals we have to parse them + // ourselves. + // FIXME: What to do if the file name is a macro? + if (Tok.is(tok::less)) { + RawLex.LexFromRawLexer(Tok); + + FilenameBuffer += '<'; + do { + if (Tok.is(tok::eod)) // Sanity check. + return false; + + if (Tok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Tok); + + // Get the string piece. + SmallVector<char, 128> TmpBuffer; + bool Invalid = false; + StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); + if (Invalid) + return false; + + FilenameBuffer += TmpName; + + RawLex.LexFromRawLexer(Tok); + } while (Tok.isNot(tok::greater)); + + FilenameBuffer += '>'; + Filename = FilenameBuffer; + } else { + if (Tok.isNot(tok::string_literal)) + return false; + + bool Invalid = false; + Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); + if (Invalid) + return false; + } + + // Lex the closing paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::r_paren)) + return false; + + // Now ask HeaderInfo if it knows about the header. + // FIXME: Subframeworks aren't handled here. Do we care? + bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); + const DirectoryLookup *CurDir; + const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId); + SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 1> + Includers; + Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir())); + const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( + Filename, SourceLocation(), isAngled, nullptr, CurDir, Includers, nullptr, + nullptr, nullptr, false); + + FileExists = File != nullptr; + return true; +} + +/// Use a raw lexer to analyze \p FileId, incrementally copying parts of it +/// and including content of included files recursively. +bool InclusionRewriter::Process(FileID FileId, + SrcMgr::CharacteristicKind FileType) +{ + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); + if (Invalid) // invalid inclusion + return false; + const char *FileName = FromFile.getBufferIdentifier(); + Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); + RawLex.SetCommentRetentionState(false); + + StringRef LocalEOL = DetectEOL(FromFile); + + // Per the GNU docs: "1" indicates entering a new file. + if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID()) + WriteLineInfo(FileName, 1, FileType, ""); + else + WriteLineInfo(FileName, 1, FileType, " 1"); + + if (SM.getFileIDSize(FileId) == 0) + return false; + + // The next byte to be copied from the source file, which may be non-zero if + // the lexer handled a BOM. + unsigned NextToWrite = SM.getFileOffset(RawLex.getSourceLocation()); + assert(SM.getLineNumber(FileId, NextToWrite) == 1); + int Line = 1; // The current input file line number. + + Token RawToken; + RawLex.LexFromRawLexer(RawToken); + + // TODO: Consider adding a switch that strips possibly unimportant content, + // such as comments, to reduce the size of repro files. + while (RawToken.isNot(tok::eof)) { + if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) { + RawLex.setParsingPreprocessorDirective(true); + Token HashToken = RawToken; + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.getIdentifierInfo() != nullptr) { + switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_include_next: + case tok::pp_import: { + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite, + Line); + if (FileId != PP.getPredefinesFileID()) + WriteLineInfo(FileName, Line - 1, FileType, ""); + StringRef LineInfoExtra; + if (const FileChange *Change = FindFileChangeLocation( + HashToken.getLocation())) { + if (Change->Mod) { + WriteImplicitModuleImport(Change->Mod); + + // else now include and recursively process the file + } else if (Process(Change->Id, Change->FileType)) { + // and set lineinfo back to this file, if the nested one was + // actually included + // `2' indicates returning to a file (after having included + // another file. + LineInfoExtra = " 2"; + } + } + // fix up lineinfo (since commented out directive changed line + // numbers) for inclusions that were skipped due to header guards + WriteLineInfo(FileName, Line, FileType, LineInfoExtra); + break; + } + case tok::pp_pragma: { + StringRef Identifier = NextIdentifierName(RawLex, RawToken); + if (Identifier == "clang" || Identifier == "GCC") { + if (NextIdentifierName(RawLex, RawToken) == "system_header") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, + NextToWrite, Line); + // update our own type + FileType = SM.getFileCharacteristic(RawToken.getLocation()); + WriteLineInfo(FileName, Line, FileType); + } + } else if (Identifier == "once") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, + NextToWrite, Line); + WriteLineInfo(FileName, Line, FileType); + } + break; + } + case tok::pp_if: + case tok::pp_elif: { + bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() == + tok::pp_elif); + // Rewrite special builtin macros to avoid pulling in host details. + do { + // Walk over the directive. + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + + if (RawToken.is(tok::identifier)) { + bool HasFile; + SourceLocation Loc = RawToken.getLocation(); + + // Rewrite __has_include(x) + if (RawToken.getIdentifierInfo()->isStr("__has_include")) { + if (!HandleHasInclude(FileId, RawLex, nullptr, RawToken, + HasFile)) + continue; + // Rewrite __has_include_next(x) + } else if (RawToken.getIdentifierInfo()->isStr( + "__has_include_next")) { + const DirectoryLookup *Lookup = PP.GetCurDirLookup(); + if (Lookup) + ++Lookup; + + if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken, + HasFile)) + continue; + } else { + continue; + } + // Replace the macro with (0) or (1), followed by the commented + // out macro for reference. + OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), + LocalEOL, Line, false); + OS << '(' << (int) HasFile << ")/*"; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, false); + OS << "*/"; + } + } while (RawToken.isNot(tok::eod)); + if (elif) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/ true); + WriteLineInfo(FileName, Line, FileType); + } + break; + } + case tok::pp_endif: + case tok::pp_else: { + // We surround every #include by #if 0 to comment it out, but that + // changes line numbers. These are fixed up right after that, but + // the whole #include could be inside a preprocessor conditional + // that is not processed. So it is necessary to fix the line + // numbers one the next line after each #else/#endif as well. + RawLex.SetKeepWhitespaceMode(true); + do { + RawLex.LexFromRawLexer(RawToken); + } while (RawToken.isNot(tok::eod) && RawToken.isNot(tok::eof)); + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/ true); + WriteLineInfo(FileName, Line, FileType); + RawLex.SetKeepWhitespaceMode(false); + } + default: + break; + } + } + RawLex.setParsingPreprocessorDirective(false); + } + RawLex.LexFromRawLexer(RawToken); + } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(SM.getLocForEndOfFile(FileId)), LocalEOL, + Line, /*EnsureNewline=*/true); + return true; +} + +/// InclusionRewriterInInput - Implement -frewrite-includes mode. +void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + SourceManager &SM = PP.getSourceManager(); + InclusionRewriter *Rewrite = new InclusionRewriter(PP, *OS, + Opts.ShowLineMarkers); + Rewrite->detectMainFileEOL(); + + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Rewrite)); + PP.IgnorePragmas(); + + // First let the preprocessor process the entire file and call callbacks. + // Callbacks will record which #include's were actually performed. + PP.EnterMainSourceFile(); + Token Tok; + // Only preprocessor directives matter here, so disable macro expansion + // everywhere else as an optimization. + // TODO: It would be even faster if the preprocessor could be switched + // to a mode where it would parse only preprocessor directives and comments, + // nothing else matters for parsing or processing. + PP.SetMacroExpansionOnlyInDirectives(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + Rewrite->setPredefinesBuffer(SM.getBuffer(PP.getPredefinesFileID())); + Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User); + Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User); + OS->flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp new file mode 100644 index 0000000..0d0a991 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp @@ -0,0 +1,217 @@ +//===--- 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/Rewrite/Frontend/Rewriters.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <memory> + +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. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); + + // 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::raw_identifier)) + PP.LookUpIdentifierInfo(RawTok); + + RawTokens.push_back(RawTok); + } while (RawTok.isNot(tok::eof)); +} + + +/// RewriteMacrosInInput - Implement -rewrite-macros mode. +void clang::RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS) { + SourceManager &SM = PP.getSourceManager(); + + Rewriter Rewrite; + Rewrite.setSourceMgr(SM, PP.getLangOpts()); + 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.getExpansionLoc(PPTok.getLocation()); + + // If PPTok is from a different source file, ignore it. + if (!SM.isWrittenInMainFile(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 (II->getName() == "warning") { + // Comment out #warning. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } else if (II->getName() == "pragma" && + RawTokens[CurRawTok+1].is(tok::identifier) && + (RawTokens[CurRawTok+1].getIdentifierInfo()->getName() == + "mark")) { + // Comment out #pragma mark. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } + } + + // 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]); + 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, "*/"); + 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.getExpansionLoc(PPTok.getLocation()); + PPOffs = SM.getFileOffset(PPLoc); + } + Expansion += ' '; + RB.InsertTextBefore(InsertPos, Expansion); + } + + // 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"); + *OS << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + fprintf(stderr, "No changes\n"); + } + OS->flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp new file mode 100644 index 0000000..47f8189 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -0,0 +1,7740 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +#ifdef CLANG_ENABLE_OBJC_REWRITER + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteModernObjC : public ASTConsumer { + protected: + + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + raw_ostream* OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + Expr *GlobalConstructionExp; + unsigned RewriteFailedDiag; + unsigned GlobalBlockRewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperConstructorFunctionDecl; + FunctionDecl *CurFunctionDef; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; + SmallVector<ObjCCategoryImplDecl *, 8> CategoryImplementation; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs; + llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCWrittenInterfaces; + llvm::SmallPtrSet<TagDecl*, 32> GlobalDefinedTags; + SmallVector<ObjCInterfaceDecl*, 32> ObjCInterfacesSeen; + /// DefinedNonLazyClasses - List of defined "non-lazy" classes. + SmallVector<ObjCInterfaceDecl*, 8> DefinedNonLazyClasses; + + /// DefinedNonLazyCategories - List of defined "non-lazy" categories. + SmallVector<ObjCCategoryDecl *, 8> DefinedNonLazyCategories; + + SmallVector<Stmt *, 32> Stmts; + SmallVector<int, 8> ObjCBcLabelNo; + // Remember all the @protocol(<expr>) expressions. + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ProtocolExprDecls; + + llvm::DenseSet<uint64_t> CopyDestroyCache; + + // Block expressions. + SmallVector<BlockExpr *, 32> Blocks; + SmallVector<int, 32> InnerDeclRefsCount; + SmallVector<DeclRefExpr *, 32> InnerDeclRefs; + + SmallVector<DeclRefExpr *, 32> BlockDeclRefs; + + + // Block related declarations. + SmallVector<ValueDecl *, 8> BlockByCopyDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDeclsPtrSet; + SmallVector<ValueDecl *, 8> BlockByRefDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDeclsPtrSet; + llvm::DenseMap<ValueDecl *, unsigned> BlockByRefDeclNo; + llvm::SmallPtrSet<ValueDecl *, 8> ImportedBlockDecls; + llvm::SmallPtrSet<VarDecl *, 8> ImportedLocalExternalDecls; + + llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; + llvm::DenseMap<ObjCInterfaceDecl *, + llvm::SmallPtrSet<ObjCIvarDecl *, 8> > ReferencedIvars; + + // ivar bitfield grouping containers + llvm::DenseSet<const ObjCInterfaceDecl *> ObjCInterefaceHasBitfieldGroups; + llvm::DenseMap<const ObjCIvarDecl* , unsigned> IvarGroupNumber; + // This container maps an <class, group number for ivar> tuple to the type + // of the struct where the bitfield belongs. + llvm::DenseMap<std::pair<const ObjCInterfaceDecl*, unsigned>, QualType> GroupRecordType; + SmallVector<FunctionDecl*, 32> FunctionDefinitionsSeen; + + // 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; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool GenerateLineInfo; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteModernObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteModernObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + void InitializeCommon(ASTContext &context); + + public: + llvm::DenseMap<ObjCMethodDecl*, std::string> MethodInternalNames; + // Top Level Driver code. + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(Class); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + if (FunctionDecl *FDecl = dyn_cast<FunctionDecl>(*I)) { + // Under modern abi, we cannot translate body of the function + // yet until all class extensions and its implementation is seen. + // This is because they may introduce new bitfields which must go + // into their grouping struct. + if (FDecl->isThisDeclarationADefinition() && + // Not c functions defined inside an objc container. + !FDecl->isTopLevelDeclInObjCContainer()) { + FunctionDefinitionsSeen.push_back(FDecl); + break; + } + } + HandleTopLevelSingleDecl(*I); + } + return true; + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(*I)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + else + RewriteObjCQualifiedInterfaceTypes(TD); + } + } + return; + } + + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteModernObjC(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn, bool LineInfo); + + ~RewriteModernObjC() {} + + void HandleTranslationUnit(ASTContext &C) override; + + void ReplaceStmt(Stmt *Old, Stmt *New) { + ReplaceStmtWithRange(Old, New, Old->getSourceRange()); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + assert(Old != nullptr && New != nullptr && "Expected non-null Stmt's"); + + Stmt *ReplacingStmt = ReplacedNodes[Old]; + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // Measure 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, nullptr, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteLineDirective(const Decl *D); + void ConvertSourceLocationToLineDirective(SourceLocation Loc, + std::string &LineString); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + std::string getIvarAccessString(ObjCIvarDecl *D); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); + Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); + Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); + Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + void RewriteImplicitCastObjCExpr(CastExpr *IE); + void RewriteLinkageSpec(LinkageSpecDecl *LSD); + + // Computes ivar bitfield group no. + unsigned ObjCIvarBitfieldGroupNo(ObjCIvarDecl *IV); + // Names field decl. for ivar bitfield group. + void ObjCIvarBitfieldGroupDecl(ObjCIvarDecl *IV, std::string &Result); + // Names struct type for ivar bitfield group. + void ObjCIvarBitfieldGroupType(ObjCIvarDecl *IV, std::string &Result); + // Names symbol for ivar bitfield group field offset. + void ObjCIvarBitfieldGroupOffset(ObjCIvarDecl *IV, std::string &Result); + // Given an ivar bitfield, it builds (or finds) its group record type. + QualType GetGroupRecordTypeForObjCIvarBitfield(ObjCIvarDecl *IV); + QualType SynthesizeBitfieldGroupStructType( + ObjCIvarDecl *IV, + SmallVectorImpl<ObjCIvarDecl *> &IVars); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result); + bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag, + bool &IsNamedDefinition); + void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result); + + bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result); + + void RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void Initialize(ASTContext &context) override; + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + Expr **args, unsigned nargs, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); + + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperConstructorFunctionDecl(); + + // Rewriting metadata + template<typename MethodIterator> + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + std::string &Result); + void RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Prots, + StringRef prefix, StringRef ClassName, std::string &Result); + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + void RewriteClassSetupInitHook(std::string &Result); + + void RewriteMetaDataIntoBuffer(std::string &Result); + void WriteImageInfo(std::string &Result); + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + void RewriteCategorySetupInitHook(std::string &Result); + + // Rewriting ivar + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts); + + // 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); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs<BlockPointerType>(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool convertObjCTypeToCStyleType(QualType &T); + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) { + bool isConst = T.isConstQualified(); + T = isConst ? Context->getObjCIdType().withConst() + : Context->getObjCIdType(); + } + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(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->getAs<PointerType>()) { + if (isa<ObjCInterfaceType>(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + ArrayRef<QualType> args, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, nullptr, + TInfo, SourceLocation(), SourceLocation()); + } + + bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const { + IdentifierInfo* II = &Context->Idents.get("load"); + Selector LoadSel = Context->Selectors.getSelector(0, &II); + return OD->getClassMethod(LoadSel) != nullptr; + } + + StringLiteral *getStringLiteral(StringRef Str) { + QualType StrType = Context->getConstantArrayType( + Context->CharTy, llvm::APInt(32, Str.size() + 1), ArrayType::Normal, + 0); + return StringLiteral::Create(*Context, Str, StringLiteral::Ascii, + /*Pascal=*/false, StrType, SourceLocation()); + } + }; + +} + +void RewriteModernObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast<FunctionProtoType>(funcType.IgnoreParens())) { + for (const auto &I : fproto->param_types()) + if (isTopLevelBlockPointerType(I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteModernObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAs<PointerType>(); + 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"; +} + +RewriteModernObjC::RewriteModernObjC(std::string inFile, raw_ostream* OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn, + bool LineInfo) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), + SilenceRewriteMacroWarning(silenceMacroWarn), GenerateLineInfo(LineInfo) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + // FIXME. This should be an error. But if block is not called, it is OK. And it + // may break including some headers. + GlobalBlockRewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting block literal declared in global scope is not implemented"); + + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +std::unique_ptr<ASTConsumer> clang::CreateModernObjCRewriter( + const std::string &InFile, raw_ostream *OS, DiagnosticsEngine &Diags, + const LangOptions &LOpts, bool SilenceRewriteMacroWarning, bool LineInfo) { + return llvm::make_unique<RewriteModernObjC>( + InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning, LineInfo); +} + +void RewriteModernObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = nullptr; + MsgSendSuperFunctionDecl = nullptr; + MsgSendStretFunctionDecl = nullptr; + MsgSendSuperStretFunctionDecl = nullptr; + MsgSendFpretFunctionDecl = nullptr; + GetClassFunctionDecl = nullptr; + GetMetaClassFunctionDecl = nullptr; + GetSuperClassFunctionDecl = nullptr; + SelGetUidFunctionDecl = nullptr; + CFStringFunctionDecl = nullptr; + ConstantStringClassReference = nullptr; + NSStringRecord = nullptr; + CurMethodDef = nullptr; + CurFunctionDef = nullptr; + GlobalVarDecl = nullptr; + GlobalConstructionExp = nullptr; + SuperStructDecl = nullptr; + ProtocolTypeDecl = nullptr; + ConstantStringDecl = nullptr; + BcLabelCount = 0; + SuperConstructorFunctionDecl = nullptr; + NumObjCStringLiterals = 0; + PropParentMap = nullptr; + CurrentBody = nullptr; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // 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->getExpansionLoc(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 (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } else if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { + // FIXME. This will not work in all situations and leaving it out + // is harmless. + // RewriteLinkageSpec(LSD); + + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = IFace->getLocStart(); + do { + if (isa<ObjCInterfaceDecl>(*DI) && + !cast<ObjCInterfaceDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(IFace); + ++DI; + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = Proto->getLocStart(); + do { + if (isa<ObjCProtocolDecl>(*DI) && + !cast<ObjCProtocolDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isWrittenInMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // 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.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl, + ObjCIvarDecl *IvarDecl, std::string &Result) { + Result += "OBJC_IVAR_$_"; + Result += IDecl->getName(); + Result += "$"; + Result += IvarDecl->getName(); +} + +std::string +RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { + const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface(); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + if (D->isBitField()) + ObjCIvarBitfieldGroupOffset(D, IvarOffsetName); + else + WriteInternalIvarName(ClassDecl, D, IvarOffsetName); + + + std::string S = "(*("; + QualType IvarT = D->getType(); + if (D->isBitField()) + IvarT = GetGroupRecordTypeForObjCIvarBitfield(D); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + std::string TypeString(castT.getAsString(Context->getPrintingPolicy())); + S += TypeString; + S += ")"; + + // ((char *)self + IVAR_OFFSET_SYMBOL_NAME) + S += "((char *)self + "; + S += IvarOffsetName; + S += "))"; + if (D->isBitField()) { + S += "."; + S += D->getNameAsString(); + } + ReferencedIvars[const_cast<ObjCInterfaceDecl *>(ClassDecl)].insert(D); + return S; +} + +/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not +/// been found in the class implementation. In this case, it must be synthesized. +static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, + ObjCPropertyDecl *PD, + bool getter) { + return getter ? !IMP->getInstanceMethod(PD->getGetterName()) + : !IMP->getInstanceMethod(PD->getSetterName()); + +} + +void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startGetterSetterLoc; + + if (PID->getLocStart().isValid()) { + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + } + else + startGetterSetterLoc = IMD ? IMD->getLocEnd() : CID->getLocEnd(); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + assert(IMD && OID && "Synthesized ivars must be attached to @implementation"); + + unsigned Attributes = PD->getPropertyAttributes(); + if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = nullptr; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(startGetterSetterLoc, Getr); + } + + if (PD->isReadOnly() || + !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }\n"; + InsertText(startGetterSetterLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + typedefString += "\n#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(); + // typedef struct { } _objc_exc_Classname; + typedefString += ";\ntypedef struct {} _objc_exc_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; +} + +void RewriteModernObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *ForwardDecl = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (I == D.begin()) { + // 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. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + else + HandleTopLevelSingleDecl(*I); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(*I), typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl( + const SmallVectorImpl<Decl *> &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(D[0]), typedefString); +} + +void RewriteModernObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteModernObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getLocStart(); + + // FIXME: handle category headers that are declared across multiple lines. + if (CatDecl->getIvarRBraceLoc().isValid()) { + ReplaceText(LocStart, 1, "/** "); + ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ "); + } + else { + ReplaceText(LocStart, 0, "// "); + } + + for (auto *I : CatDecl->properties()) + RewriteProperty(I); + + for (auto *I : CatDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : CatDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */\n"); +} + +void RewriteModernObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getLocStart(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (auto *I : PDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->class_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->properties()) + RewriteProperty(I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */\n"); + + // 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"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteModernObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteModernObjC::RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG) { + SourceLocation LocStart = DG[0]->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteModernObjC::RewriteLinkageSpec(LinkageSpecDecl *LSD) { + SourceLocation LocStart = LSD->getExternLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid extern SourceLocation"); + + ReplaceText(LocStart, 0, "// "); + if (!LSD->hasBraces()) + return; + // FIXME. We don't rewrite well if '{' is not on same line as 'extern'. + SourceLocation LocRBrace = LSD->getRBraceLoc(); + if (LocRBrace.isInvalid()) + llvm_unreachable("Invalid rbrace SourceLocation"); + ReplaceText(LocRBrace, 0, "// "); +} + +void RewriteModernObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs<PointerType>()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs<BlockPointerType>()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs<FunctionType>())) { + ResultStr += + FPRetType->getReturnType().getAsString(Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteModernObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = nullptr; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getReturnType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->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(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast<ObjCInterfaceDecl*>(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (const auto *PDecl : OMD->params()) { + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + 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->getNumParams(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} +void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast<ObjCImplementationDecl>(OID); + ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); + + if (IMD) { + if (IMD->getIvarRBraceLoc().isValid()) { + ReplaceText(IMD->getLocStart(), 1, "/** "); + ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ "); + } + else { + InsertText(IMD->getLocStart(), "// "); + } + } + else + InsertText(CID->getLocStart(), "// "); + + for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (auto *I : IMD ? IMD->property_impls() : CID->property_impls()) + RewritePropertyImplDecl(I, IMD, CID); + + InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); +} + +void RewriteModernObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(ClassDecl)) + return; + // Make sure super class's are written before current class is written. + ObjCInterfaceDecl *SuperClass = ClassDecl->getSuperClass(); + while (SuperClass) { + RewriteInterfaceDecl(SuperClass); + SuperClass = SuperClass->getSuperClass(); + } + std::string ResultStr; + if (!ObjCWrittenInterfaces.count(ClassDecl->getCanonicalDecl())) { + // we haven't seen a forward decl - generate a typedef. + RewriteOneForwardClassDecl(ClassDecl, ResultStr); + RewriteIvarOffsetSymbols(ClassDecl, ResultStr); + + RewriteObjCInternalStruct(ClassDecl, ResultStr); + // Mark this typedef as having been written into its c++ equivalent. + ObjCWrittenInterfaces.insert(ClassDecl->getCanonicalDecl()); + + for (auto *I : ClassDecl->properties()) + RewriteProperty(I); + for (auto *I : ClassDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : ClassDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */\n"); + } +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base; + SmallVector<Expr*, 2> Args; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = nullptr; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa<OpaqueValueExpr>(Arg)) + Arg = cast<OpaqueValueExpr>(Arg)->getSourceExpr(); + Arg = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // TODO: avoid this copy. + SmallVector<SourceLocation, 1> SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = nullptr; + SmallVector<Expr*, 1> Args; + { + DisableReplaceStmtScope S(*this); + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa<OpaqueValueExpr>(Arg)) + Arg = cast<OpaqueValueExpr>(Arg)->getSourceExpr(); + Arg = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // Intentionally empty. + SmallVector<SourceLocation, 1> SelLocs; + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// SynthCountByEnumWithState - To print: +/// ((NSUInteger (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, NSUInteger)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)__rw_items, (NSUInteger)16) +/// +void RewriteModernObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((_WIN_NSUInteger (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, _WIN_NSUInteger))(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 *)__rw_items, (_WIN_NSUInteger)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteModernObjC::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); + + return nullptr; +} + +void RewriteModernObjC::ConvertSourceLocationToLineDirective( + SourceLocation Loc, + std::string &LineString) { + if (Loc.isFileID() && GenerateLineInfo) { + LineString += "\n#line "; + PresumedLoc PLoc = SM->getPresumedLoc(Loc); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + LineString += "\"\n"; + } +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteModernObjC::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); + + return nullptr; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// NSUInteger limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_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:__rw_items count:16])); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteModernObjC::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); + StringRef elementName; + std::string elementTypeAsString; + std::string buf; + // line directive first. + SourceLocation ForEachLoc = S->getForLoc(); + ConvertSourceLocationToLineDirective(ForEachLoc, buf); + buf += "{\n\t"; + if (DeclStmt *DS = dyn_cast<DeclStmt>(S->getElement())) { + // type elem; + NamedDecl* D = cast<NamedDecl>(DS->getSingleDecl()); + QualType ElementType = cast<ValueDecl>(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast<DeclRefExpr>(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = cast<ValueDecl>(DR->getDecl()); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_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); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_items count:16]; + // which is synthesized into: + // NSUInteger limit = + // ((NSUInteger (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, NSUInteger)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)__rw_items, (NSUInteger)16); + buf += "_WIN_NSUInteger 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); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while ((limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_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 += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa<CompoundStmt>(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } 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.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return nullptr; +} + +static void Write_RethrowObject(std::string &buf) { + buf += "{ struct _FIN { _FIN(id reth) : rethrow(reth) {}\n"; + buf += "\t~_FIN() { if (rethrow) objc_exception_throw(rethrow); }\n"; + buf += "\tid rethrow;\n"; + buf += "\t} _fin_force_rethow(_rethrow);"; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteModernObjC::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; + SourceLocation SynchLoc = S->getAtSynchronizedLoc(); + ConvertSourceLocationToLineDirective(SynchLoc, buf); + buf += "{ id _rethrow = 0; id _sync_obj = (id)"; + + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf); + + buf = "; objc_sync_enter(_sync_obj);\n"; + buf += "try {\n\tstruct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}"; + buf += "\n\t~_SYNC_EXIT() {objc_sync_exit(sync_exit);}"; + buf += "\n\tid sync_exit;"; + buf += "\n\t} _sync_exit(_sync_obj);\n"; + + // 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 RParenExprLoc = S->getSynchBody()->getLocStart(); + const char *RParenExprLocBuf = SM->getCharacterData(RParenExprLoc); + while (*RParenExprLocBuf != ')') RParenExprLocBuf--; + RParenExprLoc = startLoc.getLocWithOffset(RParenExprLocBuf-startBuf); + + SourceLocation LBranceLoc = S->getSynchBody()->getLocStart(); + const char *LBraceLocBuf = SM->getCharacterData(LBranceLoc); + assert (*LBraceLocBuf == '{'); + ReplaceText(RParenExprLoc, (LBraceLocBuf - SM->getCharacterData(RParenExprLoc) + 1), buf); + + SourceLocation startRBraceLoc = S->getSynchBody()->getLocEnd(); + assert((*SM->getCharacterData(startRBraceLoc) == '}') && + "bogus @synchronized block"); + + buf = "} catch (id e) {_rethrow = e;}\n"; + Write_RethrowObject(buf); + buf += "}\n"; + buf += "}\n"; + + ReplaceText(startRBraceLoc, 1, buf); + + return nullptr; +} + +void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + WarnAboutReturnGotoStmts(*CI); + + if (isa<ReturnStmt>(S) || isa<GotoStmt>(S)) { + Diags.Report(Context->getFullLoc(S->getLocStart()), + TryFinallyContainsReturnDiag); + } + return; +} + +Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + SourceLocation startLoc = S->getAtLoc(); + ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */"); + ReplaceText(S->getSubStmt()->getLocStart(), 1, + "{ __AtAutoreleasePool __autoreleasepool; "); + + return nullptr; +} + +Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt(); + bool noCatch = S->getNumCatchStmts() == 0; + std::string buf; + SourceLocation TryLocation = S->getAtTryLoc(); + ConvertSourceLocationToLineDirective(TryLocation, buf); + + if (finalStmt) { + if (noCatch) + buf += "{ id volatile _rethrow = 0;\n"; + else { + buf += "{ id volatile _rethrow = 0;\ntry {\n"; + } + } + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + if (finalStmt) + ReplaceText(startLoc, 1, buf); + else + // @try -> try + ReplaceText(startLoc, 1, ""); + + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + startLoc = Catch->getLocStart(); + bool AtRemoved = false; + if (catchDecl) { + QualType t = catchDecl->getType(); + if (const ObjCObjectPointerType *Ptr = t->getAs<ObjCObjectPointerType>()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + std::string Result; + ConvertSourceLocationToLineDirective(Catch->getLocStart(), Result); + + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @catch location"); + SourceLocation rParenLoc = Catch->getRParenLoc(); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + + // _objc_exc_Foo *_e as argument to catch. + Result += "catch (_objc_exc_"; Result += IDecl->getNameAsString(); + Result += " *_"; Result += catchDecl->getNameAsString(); + Result += ")"; + ReplaceText(startLoc, rParenBuf-startBuf+1, Result); + // Foo *e = (Foo *)_e; + Result.clear(); + Result = "{ "; + Result += IDecl->getNameAsString(); + Result += " *"; Result += catchDecl->getNameAsString(); + Result += " = ("; Result += IDecl->getNameAsString(); Result += "*)"; + Result += "_"; Result += catchDecl->getNameAsString(); + + Result += "; "; + SourceLocation lBraceLoc = Catch->getCatchBody()->getLocStart(); + ReplaceText(lBraceLoc, 1, Result); + AtRemoved = true; + } + } + } + if (!AtRemoved) + // @catch -> catch + ReplaceText(startLoc, 1, ""); + + } + if (finalStmt) { + buf.clear(); + SourceLocation FinallyLoc = finalStmt->getLocStart(); + + if (noCatch) { + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + else { + buf += "}\n"; + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + + SourceLocation startFinalLoc = finalStmt->getLocStart(); + ReplaceText(startFinalLoc, 8, buf); + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startFinalBodyLoc = body->getLocStart(); + buf.clear(); + Write_RethrowObject(buf); + ReplaceText(startFinalBodyLoc, 1, buf); + + SourceLocation endFinalBodyLoc = body->getLocEnd(); + ReplaceText(endFinalBodyLoc, 1, "}\n}"); + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(S->getTryBody()); + } + + return nullptr; +} + +// 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 *RewriteModernObjC::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 + buf = "throw"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf); + + SourceLocation endLoc = S->getLocEnd(); + const char *endBuf = SM->getCharacterData(endLoc); + const char *semiBuf = strchr(endBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + if (S->getThrowExpr()) + ReplaceText(semiLoc, 1, ");"); + return nullptr; +} + +Stmt *RewriteModernObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = getStringLiteral(StrEncoding); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return Replacement; +} + +Stmt *RewriteModernObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr *RewriteModernObjC::SynthesizeCallToFunctionDecl( + FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, + SourceLocation EndLoc) { + // 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, false, msgSendType, VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, nullptr, VK_RValue); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + + CallExpr *Exp = + new (Context) CallExpr(*Context, ICE, llvm::makeArrayRef(args, nargs), + FT->getCallResultType(*Context), + VK_RValue, EndLoc); + return Exp; +} + +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 RewriteModernObjC::needToScanForQualifiers(QualType T) { + if (T->isObjCQualifiedIdType()) + return true; + if (const PointerType *PT = T->getAs<PointerType>()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + return false; +} + +void RewriteModernObjC::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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = nullptr; + 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()->getAs<FunctionType>(); + assert(funcType && "missing function type"); + proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + Type = proto->getReturnType(); + } + else if (FieldDecl *FD = dyn_cast<FieldDecl>(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(Dcl)) { + Loc = TD->getLocation(); + Type = TD->getUnderlyingType(); + } + 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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + 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->getNumParams(); i++) { + if (needToScanForQualifiers(proto->getParamType(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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + 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++; + } + } +} + +void RewriteModernObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (!isa<TypeOfExprType>(TypePtr)) + return; + while (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs<Type>(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteModernObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), ArgTys); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, + nullptr, SC_Extern); +} + +void RewriteModernObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in <objc/objc.h> + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteModernObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + +void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + QualType Type = proto->getReturnType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumParams(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getParamType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + if (FD->isVariadic()) { + FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n"; + } + else + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); +} + +// SynthSuperConstructorFunctionDecl - id __rw_objc_super(id obj, id super); +void RewriteModernObjC::SynthSuperConstructorFunctionDecl() { + if (SuperConstructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + SuperConstructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(void); +void RewriteModernObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector<QualType, 2> ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(void); +void RewriteModernObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector<QualType, 2> ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, + msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + 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 = getSimpleFunctionType(Context->DoubleTy, + ArgTys, /*isVariadic=*/true); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthGetClassFunctionDecl - Class objc_getClass(const char *name); +void RewriteModernObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteModernObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, nullptr, + SC_Extern); +} + +// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name); +void RewriteModernObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + assert (Exp != nullptr && "Expected non-null ObjCStringLiteral"); + 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 (!isAlphanumeric(c)) + 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, nullptr, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, nullptr, SC_Static); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, + SourceLocation()); + Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + + Expr *FlagExp = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, Exp->getValue()), + Context->IntTy, Exp->getLocation()); + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Context->ObjCBuiltinBoolTy, + CK_BitCast, FlagExp); + ParenExpr *PE = new (Context) ParenExpr(Exp->getLocation(), Exp->getExprLoc(), + cast); + ReplaceStmt(Exp, PE); + return PE; +} + +Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 4> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + + // Create a call to objc_getClass("<BoxingClass>"). It will be the 1st argument. + ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); + ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); + + IdentifierInfo *clsName = BoxingClass->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("<BoxingMethod>:"), etc. + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + SelExprs.push_back( + getStringLiteral(BoxingMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(subExpr)) { + QualType type = ICE->getType(); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK = CK_BitCast; + if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) + CK = CK_IntegralToBoolean; + subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); + } + MsgExprs.push_back(subExpr); + + SmallVector<QualType, 4> ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto PI : BoxingMethod->parameters()) + ArgTypes.push_back(PI->getType()); + + QualType returnType = Exp->getType(); + // 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, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, BoxingMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = new (Context) + CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSArrayFType = + getSimpleFunctionType(Context->VoidTy, IntQT, true); + std::string NSArrayFName("__NSContainer_literal"); + FunctionDecl *NSArrayFD = SynthBlockInitFunctionDecl(NSArrayFName); + DeclRefExpr *NSArrayDRE = + new (Context) DeclRefExpr(NSArrayFD, false, NSArrayFType, VK_RValue, + SourceLocation()); + + SmallVector<Expr*, 16> InitExprs; + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + InitExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) + InitExprs.push_back(Exp->getElement(i)); + Expr *NSArrayCallExpr = + new (Context) CallExpr(*Context, NSArrayDRE, InitExprs, + NSArrayFType, VK_LValue, SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), + nullptr, /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ArrayLiteralME = + new (Context) MemberExpr(NSArrayCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * ArrayLiteralObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + ArrayLiteralME); + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 32> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + ObjCMethodDecl *ArrayMethod = Exp->getArrayWithObjectsMethod(); + SelExprs.push_back( + getStringLiteral(ArrayMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(ArrayLiteralObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + + SmallVector<QualType, 4> ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto *PI : ArrayMethod->params()) + ArgTypes.push_back(PI->getType()); + + QualType returnType = Exp->getType(); + // 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, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, ArrayMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = new (Context) + CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSDictFType = + getSimpleFunctionType(Context->VoidTy, IntQT, true); + std::string NSDictFName("__NSContainer_literal"); + FunctionDecl *NSDictFD = SynthBlockInitFunctionDecl(NSDictFName); + DeclRefExpr *NSDictDRE = + new (Context) DeclRefExpr(NSDictFD, false, NSDictFType, VK_RValue, + SourceLocation()); + + SmallVector<Expr*, 16> KeyExprs; + SmallVector<Expr*, 16> ValueExprs; + + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + KeyExprs.push_back(count); + ValueExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) { + ObjCDictionaryElement Element = Exp->getKeyValueElement(i); + KeyExprs.push_back(Element.Key); + ValueExprs.push_back(Element.Value); + } + + // (const id [])objects + Expr *NSValueCallExpr = + new (Context) CallExpr(*Context, NSDictDRE, ValueExprs, + NSDictFType, VK_LValue, SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), + nullptr, /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *DictLiteralValueME = + new (Context) MemberExpr(NSValueCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * DictValueObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralValueME); + // (const id <NSCopying> [])keys + Expr *NSKeyCallExpr = + new (Context) CallExpr(*Context, NSDictDRE, KeyExprs, + NSDictFType, VK_LValue, SourceLocation()); + + MemberExpr *DictLiteralKeyME = + new (Context) MemberExpr(NSKeyCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + + CStyleCastExpr * DictKeyObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralKeyME); + + + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 32> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + ObjCMethodDecl *DictMethod = Exp->getDictWithObjectsMethod(); + SelExprs.push_back(getStringLiteral(DictMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(DictValueObjects); + + // (const id <NSCopying> [])keys + MsgExprs.push_back(DictKeyObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + + SmallVector<QualType, 8> ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto *PI : DictMethod->params()) { + QualType T = PI->getType(); + if (const PointerType* PT = T->getAs<PointerType>()) { + QualType PointeeTy = PT->getPointeeType(); + convertToUnqualifiedObjCType(PointeeTy); + T = Context->getPointerType(PointeeTy); + } + ArgTypes.push_back(T); + } + + QualType returnType = Exp->getType(); + // 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, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, DictMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = new (Context) + CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +// struct __rw_objc_super { +// struct objc_object *object; struct objc_object *superClass; +// }; +QualType RewriteModernObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__rw_objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *object; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_object *superClass; + FieldTypes[1] = Context->getObjCIdType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteModernObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), 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(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +/// getFunctionSourceLocation - returns start location of a function +/// definition. Complication arises when function has declared as +/// extern "C" or extern "C" {...} +static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R, + FunctionDecl *FD) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + return LSD->getExternLoc(); + } + if (FD->getStorageClass() != SC_None) + R.RewriteBlockLiteralFunctionDecl(FD); + return FD->getTypeSpecStartLoc(); +} + +void RewriteModernObjC::RewriteLineDirective(const Decl *D) { + + SourceLocation Location = D->getLocation(); + + if (Location.isFileID() && GenerateLineInfo) { + std::string LineString("\n#line "); + PresumedLoc PLoc = SM->getPresumedLoc(Location); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + if (isa<ObjCMethodDecl>(D)) + LineString += "\""; + else LineString += "\"\n"; + + Location = D->getLocStart(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + Location = LSD->getExternLoc(); + } + } + InsertText(Location, LineString); + } +} + +/// SynthMsgSendStretCallExpr - This routine translates message expression +/// into a call to objc_msgSend_stret() entry point. Tricky part is that +/// nil check on receiver must be performed before calling objc_msgSend_stret. +/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...) +/// msgSendType - function type of objc_msgSend_stret(...) +/// returnType - Result type of the method being synthesized. +/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type. +/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret, +/// starting with receiver. +/// Method - Method being rewritten. +Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, ArgTypes, + Method ? Method->isVariadic() + : false); + castType = Context->getPointerType(castType); + + // build type for containing the objc_msgSend_stret object. + static unsigned stretCount=0; + std::string name = "__Stret"; name += utostr(stretCount); + std::string str = + "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n"; + str += "namespace {\n"; + str += "struct "; str += name; + str += " {\n\t"; + str += name; + str += "(id receiver, SEL sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + MsgExprs[i]->getType().getAsStringInternal(ArgName, + Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + + str += ") {\n"; + str += "\t unsigned size = sizeof("; + str += returnType.getAsString(Context->getPrintingPolicy()); str += ");\n"; + + str += "\t if (size == 1 || size == 2 || size == 4 || size == 8)\n"; + + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + str+= ");\n"; + + str += "\t else if (receiver == 0)\n"; + str += "\t memset((void*)&s, 0, sizeof(s));\n"; + str += "\t else\n"; + + + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend_stret)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + str += ");\n"; + + + str += "\t}\n"; + str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy()); + str += " s;\n"; + str += "};\n};\n\n"; + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "SynthMsgSendStretCallExpr - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + + InsertText(FunLocStart, str); + ++stretCount; + + // AST for __Stretn(receiver, args).s; + IdentifierInfo *ID = &Context->Idents.get(name); + FunctionDecl *FD = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, castType, + nullptr, SC_Extern, false, false); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue, + SourceLocation()); + CallExpr *STCE = new (Context) CallExpr(*Context, DRE, MsgExprs, + castType, VK_LValue, SourceLocation()); + + FieldDecl *FieldD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("s"), + returnType, nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(STCE, false, FieldD, SourceLocation(), + FieldD->getType(), VK_LValue, + OK_Ordinary); + + return ME; +} + +Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = nullptr; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getReturnType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 8> MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector<Expr*, 4> InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, + EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperConstructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, + 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 __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct __rw_objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls); + MsgExprs.push_back(ArgExpr); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector<Expr*, 4> InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperConstructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, 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 __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo<Proto> *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, + EndLoc); + 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(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // 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(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + 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 (const auto *PI : OMD->params()) { + QualType t = PI->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : PI->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } 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, false, msgSendType, + VK_LValue, 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 = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + // If we don't have a method decl, force a variadic cast. + const ObjCMethodDecl *MD = Exp->getMethodDecl(); + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, MD ? MD->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = new (Context) + CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc); + 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. + + Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + ReplacingStmt = STCE; + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteModernObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), + Exp->getLocEnd()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteModernObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteModernObjC::HandleTranslationUnit(). +Stmt *RewriteModernObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_REFERENCE_$_" + + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), + nullptr, SC_Extern); + DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), + VK_LValue, SourceLocation()); + CastExpr *castExpr = + NoTypeInfoCStyleCastExpr( + Context, Context->getPointerType(DRE->getType()), CK_BitCast, DRE); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; + +} + +bool RewriteModernObjC::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; +} + +/// IsTagDefinedInsideClass - This routine checks that a named tagged type +/// is defined inside an objective-c class. If so, it returns true. +bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, + TagDecl *Tag, + bool &IsNamedDefinition) { + if (!IDecl) + return false; + SourceLocation TagLocation; + if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) { + RD = RD->getDefinition(); + if (!RD || !RD->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = RD->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + } + if (EnumDecl *ED = dyn_cast<EnumDecl>(Tag)) { + if (!ED || !ED->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = ED->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + + } + return false; +} + +/// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer. +/// It handles elaborated types, as well as enum types in the process. +bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, + std::string &Result) { + if (isa<TypedefType>(Type)) { + Result += "\t"; + return false; + } + + if (Type->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(Type); + return RewriteObjCFieldDeclType(ElemTy, Result); + } + else if (Type->isRecordType()) { + RecordDecl *RD = Type->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) { + if (RD->isStruct()) + Result += "\n\tstruct "; + else if (RD->isUnion()) + Result += "\n\tunion "; + else + assert(false && "class not allowed as an ivar type"); + + Result += RD->getName(); + if (GlobalDefinedTags.count(RD)) { + // struct/union is defined globally, use it. + Result += " "; + return true; + } + Result += " {\n"; + for (auto *FD : RD->fields()) + RewriteObjCFieldDecl(FD, Result); + Result += "\t} "; + return true; + } + } + else if (Type->isEnumeralType()) { + EnumDecl *ED = Type->getAs<EnumType>()->getDecl(); + if (ED->isCompleteDefinition()) { + Result += "\n\tenum "; + Result += ED->getName(); + if (GlobalDefinedTags.count(ED)) { + // Enum is globall defined, use it. + Result += " "; + return true; + } + + Result += " {\n"; + for (const auto *EC : ED->enumerators()) { + Result += "\t"; Result += EC->getName(); Result += " = "; + llvm::APSInt Val = EC->getInitVal(); + Result += Val.toString(10); + Result += ",\n"; + } + Result += "\t} "; + return true; + } + } + + Result += "\t"; + convertObjCTypeToCStyleType(Type); + return false; +} + + +/// RewriteObjCFieldDecl - This routine rewrites a field into the buffer. +/// It handles elaborated types, as well as enum types in the process. +void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + std::string Name = fieldDecl->getNameAsString(); + + bool EleboratedType = RewriteObjCFieldDeclType(Type, Result); + if (!EleboratedType) + Type.getAsStringInternal(Name, Context->getPrintingPolicy()); + Result += Name; + if (fieldDecl->isBitField()) { + Result += " : "; Result += utostr(fieldDecl->getBitWidthValue(*Context)); + } + else if (EleboratedType && Type->isArrayType()) { + const ArrayType *AT = Context->getAsArrayType(Type); + do { + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) { + Result += "["; + llvm::APInt Dim = CAT->getSize(); + Result += utostr(Dim.getZExtValue()); + Result += "]"; + } + AT = Context->getAsArrayType(AT->getElementType()); + } while (AT); + } + + Result += ";\n"; +} + +/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined +/// named aggregate types into the input buffer. +void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + if (isa<TypedefType>(Type)) + return; + if (Type->isArrayType()) + Type = Context->getBaseElementType(Type); + ObjCContainerDecl *IDecl = + dyn_cast<ObjCContainerDecl>(fieldDecl->getDeclContext()); + + TagDecl *TD = nullptr; + if (Type->isRecordType()) { + TD = Type->getAs<RecordType>()->getDecl(); + } + else if (Type->isEnumeralType()) { + TD = Type->getAs<EnumType>()->getDecl(); + } + + if (TD) { + if (GlobalDefinedTags.count(TD)) + return; + + bool IsNamedDefinition = false; + if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) { + RewriteObjCFieldDeclType(Type, Result); + Result += ";"; + } + if (IsNamedDefinition) + GlobalDefinedTags.insert(TD); + } + +} + +unsigned RewriteModernObjC::ObjCIvarBitfieldGroupNo(ObjCIvarDecl *IV) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + if (ObjCInterefaceHasBitfieldGroups.count(CDecl)) { + return IvarGroupNumber[IV]; + } + unsigned GroupNo = 0; + SmallVector<const ObjCIvarDecl *, 8> IVars; + for (const ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + IVars.push_back(IVD); + + for (unsigned i = 0, e = IVars.size(); i < e; i++) + if (IVars[i]->isBitField()) { + IvarGroupNumber[IVars[i++]] = ++GroupNo; + while (i < e && IVars[i]->isBitField()) + IvarGroupNumber[IVars[i++]] = GroupNo; + if (i < e) + --i; + } + + ObjCInterefaceHasBitfieldGroups.insert(CDecl); + return IvarGroupNumber[IV]; +} + +QualType RewriteModernObjC::SynthesizeBitfieldGroupStructType( + ObjCIvarDecl *IV, + SmallVectorImpl<ObjCIvarDecl *> &IVars) { + std::string StructTagName; + ObjCIvarBitfieldGroupType(IV, StructTagName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get(StructTagName)); + for (unsigned i=0, e = IVars.size(); i < e; i++) { + ObjCIvarDecl *Ivar = IVars[i]; + RD->addDecl(FieldDecl::Create(*Context, RD, SourceLocation(), SourceLocation(), + &Context->Idents.get(Ivar->getName()), + Ivar->getType(), + nullptr, /*Expr *BW */Ivar->getBitWidth(), + false, ICIS_NoInit)); + } + RD->completeDefinition(); + return Context->getTagDeclType(RD); +} + +QualType RewriteModernObjC::GetGroupRecordTypeForObjCIvarBitfield(ObjCIvarDecl *IV) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + std::pair<const ObjCInterfaceDecl*, unsigned> tuple = std::make_pair(CDecl, GroupNo); + if (GroupRecordType.count(tuple)) + return GroupRecordType[tuple]; + + SmallVector<ObjCIvarDecl *, 8> IVars; + for (const ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + if (IVD->isBitField()) + IVars.push_back(const_cast<ObjCIvarDecl *>(IVD)); + else { + if (!IVars.empty()) { + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IVars[0]); + // Generate the struct type for this group of bitfield ivars. + GroupRecordType[std::make_pair(CDecl, GroupNo)] = + SynthesizeBitfieldGroupStructType(IVars[0], IVars); + IVars.clear(); + } + } + } + if (!IVars.empty()) { + // Do the last one. + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IVars[0]); + GroupRecordType[std::make_pair(CDecl, GroupNo)] = + SynthesizeBitfieldGroupStructType(IVars[0], IVars); + } + QualType RetQT = GroupRecordType[tuple]; + assert(!RetQT.isNull() && "GetGroupRecordTypeForObjCIvarBitfield struct type is NULL"); + + return RetQT; +} + +/// ObjCIvarBitfieldGroupDecl - Names field decl. for ivar bitfield group. +/// Name would be: classname__GRBF_n where n is the group number for this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupDecl(ObjCIvarDecl *IV, + std::string &Result) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + Result += CDecl->getName(); + Result += "__GRBF_"; + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + Result += utostr(GroupNo); + return; +} + +/// ObjCIvarBitfieldGroupType - Names struct type for ivar bitfield group. +/// Name of the struct would be: classname__T_n where n is the group number for +/// this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupType(ObjCIvarDecl *IV, + std::string &Result) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + Result += CDecl->getName(); + Result += "__T_"; + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + Result += utostr(GroupNo); + return; +} + +/// ObjCIvarBitfieldGroupOffset - Names symbol for ivar bitfield group field offset. +/// Name would be: OBJC_IVAR_$_classname__GRBF_n where n is the group number for +/// this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupOffset(ObjCIvarDecl *IV, + std::string &Result) { + Result += "OBJC_IVAR_$_"; + ObjCIvarBitfieldGroupDecl(IV, Result); +} + +#define SKIP_BITFIELDS(IX, ENDIX, VEC) { \ + while ((IX < ENDIX) && VEC[IX]->isBitField()) \ + ++IX; \ + if (IX < ENDIX) \ + --IX; \ +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "Name missing in SynthesizeObjCInternalStruct"); + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + SmallVector<ObjCIvarDecl *, 8> IVars; + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + IVars.push_back(IVD); + + SourceLocation LocStart = CDecl->getLocStart(); + SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); + + 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->isThisDeclarationADefinition() || IVars.size() == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // Insert named struct/union definitions inside class to + // outer scope. This follows semantics of locally defined + // struct/unions in objective-c classes. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + RewriteLocallyDefinedNamedAggregates(IVars[i], Result); + + // Insert named structs which are syntheized to group ivar bitfields + // to outer scope as well. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + if (IVars[i]->isBitField()) { + ObjCIvarDecl *IV = IVars[i]; + QualType QT = GetGroupRecordTypeForObjCIvarBitfield(IV); + RewriteObjCFieldDeclType(QT, Result); + Result += ";"; + // skip over ivar bitfields in this group. + SKIP_BITFIELDS(i , e, IVars); + } + + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + Result += "_IMPL {\n"; + + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result += "\tstruct "; Result += RCDecl->getNameAsString(); + Result += "_IMPL "; Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + } + + for (unsigned i = 0, e = IVars.size(); i < e; i++) { + if (IVars[i]->isBitField()) { + ObjCIvarDecl *IV = IVars[i]; + Result += "\tstruct "; + ObjCIvarBitfieldGroupType(IV, Result); Result += " "; + ObjCIvarBitfieldGroupDecl(IV, Result); Result += ";\n"; + // skip over ivar bitfields in this group. + SKIP_BITFIELDS(i , e, IVars); + } + else + RewriteObjCFieldDecl(IVars[i], Result); + } + + Result += "};\n"; + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl).second) + llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct"); +} + +/// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which +/// have been referenced in an ivar access expression. +void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result) { + // write out ivar offset symbols which have been referenced in an ivar + // access expression. + llvm::SmallPtrSet<ObjCIvarDecl *, 8> Ivars = ReferencedIvars[CDecl]; + if (Ivars.empty()) + return; + + llvm::DenseSet<std::pair<const ObjCInterfaceDecl*, unsigned> > GroupSymbolOutput; + for (ObjCIvarDecl *IvarDecl : Ivars) { + const ObjCInterfaceDecl *IDecl = IvarDecl->getContainingInterface(); + unsigned GroupNo = 0; + if (IvarDecl->isBitField()) { + GroupNo = ObjCIvarBitfieldGroupNo(IvarDecl); + if (GroupSymbolOutput.count(std::make_pair(IDecl, GroupNo))) + continue; + } + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + Result += "extern \"C\" "; + if (LangOpts.MicrosoftExt && + IvarDecl->getAccessControl() != ObjCIvarDecl::Private && + IvarDecl->getAccessControl() != ObjCIvarDecl::Package) + Result += "__declspec(dllimport) "; + + Result += "unsigned long "; + if (IvarDecl->isBitField()) { + ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + GroupSymbolOutput.insert(std::make_pair(IDecl, GroupNo)); + } + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ";"; + } +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteModernObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *OIMP = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = OIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(OIMP); + } + + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *CIMP = CategoryImplementation[i]; + ObjCInterfaceDecl *CDecl = CIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(CIMP); + } +} + +void RewriteModernObjC::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteModernObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getReturnType(); + std::string StructRef = "struct " + Tag; + SourceLocation BlockLoc = CE->getExprLoc(); + std::string S; + ConvertSourceLocationToLineDirective(BlockLoc, S); + + S += "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_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(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + 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 (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // 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())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + 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 RewriteModernObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef 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 (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_assign((void*)&dst->"; + S += VD->getNameAsString(); + S += ", (void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_dispose((void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::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 { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n size_t reserved;\n"; + S += " size_t Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + continue; + } + + if (!BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + if (GlobalConstructionExp) { + // extra fancy dance for global literal expression. + + // Always the latest block expression on the block stack. + std::string Tag = "__"; + Tag += FunName; + Tag += "_block_impl_"; + Tag += utostr(Blocks.size()-1); + std::string globalBuf = "static "; + globalBuf += Tag; globalBuf += " "; + std::string SStr; + + llvm::raw_string_ostream constructorExprBuf(SStr); + GlobalConstructionExp->printPretty(constructorExprBuf, nullptr, + PrintingPolicy(LangOpts)); + globalBuf += constructorExprBuf.str(); + globalBuf += ";\n"; + InsertText(FunLocStart, globalBuf); + GlobalConstructionExp = nullptr; + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = + (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD) + : FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(":", loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + //SourceLocation FunLocStart = MD->getLocStart(); + SourceLocation FunLocStart = MD->getLocStart(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteModernObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) + // FIXME: Handle enums. + BlockDeclRefs.push_back(DRE); + + return; +} + +void RewriteModernObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) { + InnerContexts.insert(cast<DeclContext>(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(*CI, + InnerBlockDeclRefs, + InnerContexts); + + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = cast<VarDecl>(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } + + return; +} + +/// convertObjCTypeToCStyleType - This routine converts such objc types +/// as qualified objects, and blocks to their closest c/c++ types that +/// it can. It returns true if input type was modified. +bool RewriteModernObjC::convertObjCTypeToCStyleType(QualType &T) { + QualType oldT = T; + convertBlockPointerToFunctionPointer(T); + if (T->isFunctionPointerType()) { + QualType PointeeTy; + if (const PointerType* PT = T->getAs<PointerType>()) { + PointeeTy = PT->getPointeeType(); + if (const FunctionType *FT = PointeeTy->getAs<FunctionType>()) { + T = convertFunctionTypeOfBlocks(FT); + T = Context->getPointerType(T); + } + } + } + + convertToUnqualifiedObjCType(T); + return T != oldT; +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteModernObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + QualType Res = FT->getReturnType(); + bool modified = convertObjCTypeToCStyleType(Res); + + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertObjCTypeToCStyleType(t)) + modified = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + if (modified) + FuncType = getSimpleFunctionType(Res, ArgTypes); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = nullptr; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(BlockExp)) { + CPT = DRE->getType()->getAs<BlockPointerType>(); + } else if (const MemberExpr *MExpr = dyn_cast<MemberExpr>(BlockExp)) { + CPT = MExpr->getType()->getAs<BlockPointerType>(); + } + else if (const ParenExpr *PRE = dyn_cast<ParenExpr>(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast<ImplicitCastExpr>(BlockExp)) + CPT = IEXPR->getType()->getAs<BlockPointerType>(); + else if (const ConditionalOperator *CEXPR = + dyn_cast<ConditionalOperator>(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast<Expr>(LHSStmt), + SourceLocation(), cast<Expr>(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { + CPT = IRE->getType()->getAs<BlockPointerType>(); + } else if (const PseudoObjectExpr *POE + = dyn_cast<PseudoObjectExpr>(BlockExp)) { + CPT = POE->getType()->castAs<BlockPointerType>(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs<FunctionType>(); + 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, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType = getSimpleFunctionType(Exp->getType(), ArgTypes); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast<Expr*>(BlockExp)); + // 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, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + 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, + Exp->getType(), VK_RValue, + SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// DeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a DeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, + FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteModernObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation()); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return PE; +} + +void RewriteModernObjC::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); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*"); + break; + } + } + return; +} + +void RewriteModernObjC::RewriteImplicitCastObjCExpr(CastExpr *IC) { + CastKind CastKind = IC->getCastKind(); + if (CastKind != CK_BlockPointerToObjCPointerCast && + CastKind != CK_AnyPointerToBlockPointerCast) + return; + + QualType QT = IC->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + std::string TypeString(QT.getAsString(Context->getPrintingPolicy())); + std::string Str = "("; + Str += TypeString; + Str += ")"; + InsertText(IC->getSubExpr()->getLocStart(), &Str[0], Str.size()); + + return; +} + +void RewriteModernObjC::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.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteModernObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) + if (isTopLevelBlockPointerType(I)) + return true; + } + return false; +} + +bool RewriteModernObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) { + if (I->isObjCQualifiedIdType()) + return true; + if (I->isObjCObjectPointerType() && + I->getPointeeType()->isObjCQualifiedInterfaceType()) + return true; + } + + } + return false; +} + +void RewriteModernObjC::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 RewriteModernObjC::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 (TypedefNameDecl *TDD = dyn_cast<TypedefNameDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("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--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *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. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id<P> with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); + + return; +} + + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast<unsigned>(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl, + bool lastDecl) { + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty, ND); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + InsertText(FunLocStart, ByrefType); + + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + Preamble += HF; + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != nullptr); + // FIXME. rewriter does not support __block c++ objects which + // require construction. + if (hasInit) + if (CXXConstructExpr *CExp = dyn_cast<CXXConstructExpr>(ND->getInit())) { + CXXConstructorDecl *CXXDecl = CExp->getConstructor(); + if (CXXDecl && CXXDecl->isDefaultConstructor()) + hasInit = false; + } + + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + + if (!firstDecl) { + // In multiple __block declarations, and for all but 1st declaration, + // find location of the separating comma. This would be start location + // where new text is to be inserted. + DeclLoc = ND->getLocation(); + const char *startDeclBuf = SM->getCharacterData(DeclLoc); + const char *commaBuf = startDeclBuf; + while (*commaBuf != ',') + commaBuf--; + assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','"); + DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf); + startBuf = commaBuf; + } + + if (!hasInit) { + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is aleady + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + ByrefType += ", "; + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + const char separator = lastDecl ? ';' : ','; + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *separatorBuf = strchr(startInitializerBuf, separator); + assert((*separatorBuf == separator) && + "RewriteByRefVar: can't find ';' or ','"); + SourceLocation separatorLoc = + startLoc.getLocWithOffset(separatorBuf-startInitializerBuf); + + InsertText(separatorLoc, lastDecl ? "}" : "};\n"); + } + return; +} + +void RewriteModernObjC::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]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteModernObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, nullptr, SC_Extern, + false, false); +} + +Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs) { + + const BlockDecl *block = Exp->getBlockDecl(); + + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + bool GlobalBlockExpr = + block->getDeclContext()->getRedeclContext()->isFileContext(); + + if (GlobalBlockExpr && !GlobalVarDecl) { + Diags.Report(block->getLocation(), GlobalBlockRewriteFailedDiag); + GlobalBlockExpr = false; + } + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a constructor call... + std::string Tag; + + if (GlobalBlockExpr) + Tag = "__global_"; + else + Tag = "__"; + Tag += FuncName + "_block_impl_" + BlockNumber; + + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, + SourceLocation()); + + SmallVector<Expr*, 4> InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData.c_str()), + Context->VoidPtrTy, nullptr, + SC_Static); + UnaryOperator *DescRefExpr = + new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, + Context->VoidPtrTy, + VK_LValue, + SourceLocation()), + UO_AddrOf, + Context->getPointerType(Context->VoidPtrTy), + VK_RValue, OK_Ordinary, + SourceLocation()); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (const auto &CI : block->captures()) { + const VarDecl *variable = CI.getVariable(); + if (variable == ND && CI.isNested()) { + assert (CI.isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = new (Context) CallExpr(*Context, DRE, InitExprs, + FType, VK_LValue, SourceLocation()); + + if (GlobalBlockExpr) { + assert (!GlobalConstructionExp && + "SynthBlockInitExpr - GlobalConstructionExp must be null"); + GlobalConstructionExp = NewRep; + NewRep = DRE; + } + + NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + // Put Paren around the call. + NewRep = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + NewRep); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteModernObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast<ObjCForCollectionStmt>(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteModernObjC::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); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) { + if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + else if (isa<OpaqueValueExpr>(S)) + S = cast<OpaqueValueExpr>(S)->getSourceExpr(); + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + Stmt *childStmt = (*CI); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + *CI = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + SmallVector<DeclRefExpr *, 8> InnerBlockDeclRefs; + llvm::SmallPtrSet<const DeclContext *, 8> InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = nullptr; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = nullptr; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast<ObjCStringLiteral>(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast<ObjCBoolLiteralExpr>(S)) + return RewriteObjCBoolLiteralExpr(BoolLitExpr); + + if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(S)) + return RewriteObjCBoxedExpr(BoxedExpr); + + if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast<ObjCArrayLiteral>(S)) + return RewriteObjCArrayLiteralExpr(ArrayLitExpr); + + if (ObjCDictionaryLiteral *DictionaryLitExpr = + dyn_cast<ObjCDictionaryLiteral>(S)) + return RewriteObjCDictionaryLiteralExpr(DictionaryLitExpr); + + 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 (ObjCAutoreleasePoolStmt *StmtAutoRelease = + dyn_cast<ObjCAutoreleasePoolStmt>(S)) { + return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease); + } + + 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. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id <FooProtocol> index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + 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 (VarDecl *VD = dyn_cast<VarDecl>(SD)) { + if (VD->hasAttr<BlocksAttr>()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE)); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(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 (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr<BlocksAttr>()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) { + RewriteCastExpr(CE); + } + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { + RewriteImplicitCastObjCExpr(ICE); + } +#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; +} + +void RewriteModernObjC::RewriteRecordBody(RecordDecl *RD) { + for (auto *FD : RD->fields()) { + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = 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 (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { + CurFunctionDef = FD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + RewriteLineDirective(D); + CurFunctionDef = nullptr; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + InsertBlockLiteralsWithinMethod(MD); + RewriteLineDirective(D); + CurMethodDef = nullptr; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast<ObjCImplementationDecl>(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast<ObjCCategoryImplDecl>(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = 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); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = nullptr; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + else + RewriteObjCQualifiedInterfaceTypes(TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast<RecordDecl>(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +/// Write_ProtocolExprReferencedMetadata - This routine writer out the +/// protocol reference symbols in the for of: +/// struct _protocol_t *PROTOCOL_REF = &PROTOCOL_METADATA. +static void Write_ProtocolExprReferencedMetadata(ASTContext *Context, + ObjCProtocolDecl *PDecl, + std::string &Result) { + // Also output .objc_protorefs$B section and its meta-data. + if (Context->getLangOpts().MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_PROTOCOL_REFERENCE_$_"; + Result += PDecl->getNameAsString(); + Result += " = &"; + Result += "_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; +} + +void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + for (unsigned i = 0, e = FunctionDefinitionsSeen.size(); i < e; i++) { + // translation of function bodies were postponed until all class and + // their extensions and implementations are seen. This is because, we + // cannot build grouping structs for bitfields until they are all seen. + FunctionDecl *FDecl = FunctionDefinitionsSeen[i]; + HandleTopLevelSingleDecl(FDecl); + } + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(<expr>). + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) { + RewriteObjCProtocolMetaData(ProtDecl, Preamble); + Write_ProtocolExprReferencedMetadata(Context, ProtDecl, Preamble); + } + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) { + ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i]; + // Write struct declaration for the class matching its ivar declarations. + // Note that for modern abi, this is postponed until the end of TU + // because class extensions and the implementation might declare their own + // private ivars. + RewriteInterfaceDecl(CDecl); + } + + // 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 { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + // Emit ImageInfo; + { + std::string ResultStr; + WriteImageInfo(ResultStr); + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteModernObjC::Initialize(ASTContext &context) { + InitializeCommon(context); + + Preamble += "#ifndef __OBJC2__\n"; + Preamble += "#define __OBJC2__\n"; + Preamble += "#endif\n"; + + // 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 { \n\tstruct objc_object *object; "; + Preamble += "\n\tstruct objc_object *superClass; "; + // Add a constructor for creating temporary objects. + Preamble += "\n\t__rw_objc_super(struct objc_object *o, struct objc_object *s) "; + Preamble += ": object(o), superClass(s) {} "; + Preamble += "\n};\n"; + + if (LangOpts.MicrosoftExt) { + // Define all sections using syntax that makes sense. + // These are currently generated. + Preamble += "\n#pragma section(\".objc_classlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_catlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n"; + // These are generated but not necessary for functionality. + Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_ivar$B\", long, read, write)\n"; + + // These need be generated for performance. Currently they are not, + // using API calls instead. + Preamble += "#pragma section(\".objc_selrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_classrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_superrefs$B\", long, read, write)\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.MicrosoftExt) { + 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 void objc_msgSend(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n"; + + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifdef _WIN64\n"; + Preamble += "typedef unsigned long long _WIN_NSUInteger;\n"; + Preamble += "#else\n"; + Preamble += "typedef unsigned int _WIN_NSUInteger;\n"; + Preamble += "#endif\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 += "#if _WIN64\n"; + Preamble += " long long length;\n"; + Preamble += "#else\n"; + Preamble += " long length;\n"; + Preamble += "#endif\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 Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __weak\n"; + Preamble += "#define __weak\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __block\n"; + Preamble += "#define __block\n"; + Preamble += "#endif\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + + // Declarations required for modern objective-c array and dictionary literals. + Preamble += "\n#include <stdarg.h>\n"; + Preamble += "struct __NSContainer_literal {\n"; + Preamble += " void * *arr;\n"; + Preamble += " __NSContainer_literal (unsigned int count, ...) {\n"; + Preamble += "\tva_list marker;\n"; + Preamble += "\tva_start(marker, count);\n"; + Preamble += "\tarr = new void *[count];\n"; + Preamble += "\tfor (unsigned i = 0; i < count; i++)\n"; + Preamble += "\t arr[i] = va_arg(marker, void *);\n"; + Preamble += "\tva_end( marker );\n"; + Preamble += " };\n"; + Preamble += " ~__NSContainer_literal() {\n"; + Preamble += "\tdelete[] arr;\n"; + Preamble += " }\n"; + Preamble += "};\n"; + + // Declaration required for implementation of @autoreleasepool statement. + Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n"; + Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n"; + Preamble += "struct __AtAutoreleasePool {\n"; + Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n"; + Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n"; + Preamble += " void * atautoreleasepoolobj;\n"; + Preamble += "};\n"; + + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This rutine synthesizes computation of +/// ivar offset. +void RewriteModernObjC::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) { + Result += "__OFFSETOFIVAR__(struct "; + Result += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + if (ivar->isBitField()) + ObjCIvarBitfieldGroupDecl(ivar, Result); + else + Result += ivar->getNameAsString(); + Result += ")"; +} + +/// WriteModernMetadataDeclarations - Writes out metadata declarations for modern ABI. +/// struct _prop_t { +/// const char *name; +/// char *attributes; +/// } + +/// struct _prop_list_t { +/// uint32_t entsize; // sizeof(struct _prop_t) +/// uint32_t count_of_properties; +/// struct _prop_t prop_list[count_of_properties]; +/// } + +/// struct _protocol_t; + +/// struct _protocol_list_t { +/// long protocol_count; // Note, this is 32/64 bit +/// struct _protocol_t * protocol_list[protocol_count]; +/// } + +/// struct _objc_method { +/// SEL _cmd; +/// const char *method_type; +/// char *_imp; +/// } + +/// struct _method_list_t { +/// uint32_t entsize; // sizeof(struct _objc_method) +/// uint32_t method_count; +/// struct _objc_method method_list[method_count]; +/// } + +/// struct _protocol_t { +/// id isa; // NULL +/// const char *protocol_name; +/// const struct _protocol_list_t * protocol_list; // super protocols +/// const struct method_list_t *instance_methods; +/// const struct method_list_t *class_methods; +/// const struct method_list_t *optionalInstanceMethods; +/// const struct method_list_t *optionalClassMethods; +/// const struct _prop_list_t * properties; +/// const uint32_t size; // sizeof(struct _protocol_t) +/// const uint32_t flags; // = 0 +/// const char ** extendedMethodTypes; +/// } + +/// struct _ivar_t { +/// unsigned long int *offset; // pointer to ivar offset location +/// const char *name; +/// const char *type; +/// uint32_t alignment; +/// uint32_t size; +/// } + +/// struct _ivar_list_t { +/// uint32 entsize; // sizeof(struct _ivar_t) +/// uint32 count; +/// struct _ivar_t list[count]; +/// } + +/// struct _class_ro_t { +/// uint32_t flags; +/// uint32_t instanceStart; +/// uint32_t instanceSize; +/// uint32_t reserved; // only when building for 64bit targets +/// const uint8_t *ivarLayout; +/// const char *name; +/// const struct _method_list_t *baseMethods; +/// const struct _protocol_list_t *baseProtocols; +/// const struct _ivar_list_t *ivars; +/// const uint8_t *weakIvarLayout; +/// const struct _prop_list_t *properties; +/// } + +/// struct _class_t { +/// struct _class_t *isa; +/// struct _class_t *superclass; +/// void *cache; +/// IMP *vtable; +/// struct _class_ro_t *ro; +/// } + +/// struct _category_t { +/// const char *name; +/// struct _class_t *cls; +/// const struct _method_list_t *instance_methods; +/// const struct _method_list_t *class_methods; +/// const struct _protocol_list_t *protocols; +/// const struct _prop_list_t *properties; +/// } + +/// MessageRefTy - LLVM for: +/// struct _message_ref_t { +/// IMP messenger; +/// SEL name; +/// }; + +/// SuperMessageRefTy - LLVM for: +/// struct _super_message_ref_t { +/// SUPER_IMP messenger; +/// SEL name; +/// }; + +static void WriteModernMetadataDeclarations(ASTContext *Context, std::string &Result) { + static bool meta_data_declared = false; + if (meta_data_declared) + return; + + Result += "\nstruct _prop_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *attributes;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t;\n"; + + Result += "\nstruct _objc_method {\n"; + Result += "\tstruct objc_selector * _cmd;\n"; + Result += "\tconst char *method_type;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t {\n"; + Result += "\tvoid * isa; // NULL\n"; + Result += "\tconst char *protocol_name;\n"; + Result += "\tconst struct _protocol_list_t * protocol_list; // super protocols\n"; + Result += "\tconst struct method_list_t *instance_methods;\n"; + Result += "\tconst struct method_list_t *class_methods;\n"; + Result += "\tconst struct method_list_t *optionalInstanceMethods;\n"; + Result += "\tconst struct method_list_t *optionalClassMethods;\n"; + Result += "\tconst struct _prop_list_t * properties;\n"; + Result += "\tconst unsigned int size; // sizeof(struct _protocol_t)\n"; + Result += "\tconst unsigned int flags; // = 0\n"; + Result += "\tconst char ** extendedMethodTypes;\n"; + Result += "};\n"; + + Result += "\nstruct _ivar_t {\n"; + Result += "\tunsigned long int *offset; // pointer to ivar offset location\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *type;\n"; + Result += "\tunsigned int alignment;\n"; + Result += "\tunsigned int size;\n"; + Result += "};\n"; + + Result += "\nstruct _class_ro_t {\n"; + Result += "\tunsigned int flags;\n"; + Result += "\tunsigned int instanceStart;\n"; + Result += "\tunsigned int instanceSize;\n"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + Result += "\tunsigned int reserved;\n"; + Result += "\tconst unsigned char *ivarLayout;\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst struct _method_list_t *baseMethods;\n"; + Result += "\tconst struct _objc_protocol_list *baseProtocols;\n"; + Result += "\tconst struct _ivar_list_t *ivars;\n"; + Result += "\tconst unsigned char *weakIvarLayout;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "\nstruct _class_t {\n"; + Result += "\tstruct _class_t *isa;\n"; + Result += "\tstruct _class_t *superclass;\n"; + Result += "\tvoid *cache;\n"; + Result += "\tvoid *vtable;\n"; + Result += "\tstruct _class_ro_t *ro;\n"; + Result += "};\n"; + + Result += "\nstruct _category_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _class_t *cls;\n"; + Result += "\tconst struct _method_list_t *instance_methods;\n"; + Result += "\tconst struct _method_list_t *class_methods;\n"; + Result += "\tconst struct _protocol_list_t *protocols;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "extern \"C\" __declspec(dllimport) struct objc_cache _objc_empty_cache;\n"; + Result += "#pragma warning(disable:4273)\n"; + meta_data_declared = true; +} + +static void Write_protocol_list_t_TypeDecl(std::string &Result, + long super_protocol_count) { + Result += "struct /*_protocol_list_t*/"; Result += " {\n"; + Result += "\tlong protocol_count; // Note, this is 32/64 bit\n"; + Result += "\tstruct _protocol_t *super_protocols["; + Result += utostr(super_protocol_count); Result += "];\n"; + Result += "}"; +} + +static void Write_method_list_t_TypeDecl(std::string &Result, + unsigned int method_count) { + Result += "struct /*_method_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _objc_method)\n"; + Result += "\tunsigned int method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(method_count); Result += "];\n"; + Result += "}"; +} + +static void Write__prop_list_t_TypeDecl(std::string &Result, + unsigned int property_count) { + Result += "struct /*_prop_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count_of_properties;\n"; + Result += "\tstruct _prop_t prop_list["; + Result += utostr(property_count); Result += "];\n"; + Result += "}"; +} + +static void Write__ivar_list_t_TypeDecl(std::string &Result, + unsigned int ivar_count) { + Result += "struct /*_ivar_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count;\n"; + Result += "\tstruct _ivar_t ivar_list["; + Result += utostr(ivar_count); Result += "];\n"; + Result += "}"; +} + +static void Write_protocol_list_initializer(ASTContext *Context, std::string &Result, + ArrayRef<ObjCProtocolDecl *> SuperProtocols, + StringRef VarName, + StringRef ProtocolName) { + if (SuperProtocols.size() > 0) { + Result += "\nstatic "; + Write_protocol_list_t_TypeDecl(Result, SuperProtocols.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += utostr(SuperProtocols.size()); Result += ",\n"; + for (unsigned i = 0, e = SuperProtocols.size(); i < e; i++) { + ObjCProtocolDecl *SuperPD = SuperProtocols[i]; + Result += "\t&"; Result += "_OBJC_PROTOCOL_"; + Result += SuperPD->getNameAsString(); + if (i == e-1) + Result += "\n};\n"; + else + Result += ",\n"; + } + } +} + +static void Write_method_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCMethodDecl *> Methods, + StringRef VarName, + StringRef TopLevelDeclName, + bool MethodImpl) { + if (Methods.size() > 0) { + Result += "\nstatic "; + Write_method_list_t_TypeDecl(Result, Methods.size()); + Result += " "; Result += VarName; + Result += TopLevelDeclName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_objc_method)"; Result += ",\n"; + Result += "\t"; Result += utostr(Methods.size()); Result += ",\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + if (i == 0) + Result += "\t{{(struct objc_selector *)\""; + else + Result += "\t{(struct objc_selector *)\""; + Result += (MD)->getSelector().getAsString(); Result += "\""; + Result += ", "; + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(MD, MethodTypeString); + Result += "\""; Result += MethodTypeString; Result += "\""; + Result += ", "; + if (!MethodImpl) + Result += "0"; + else { + Result += "(void *)"; + Result += RewriteObj.MethodInternalNames[MD]; + } + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +static void Write_prop_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCPropertyDecl *> Properties, + const Decl *Container, + StringRef VarName, + StringRef ProtocolName) { + if (Properties.size() > 0) { + Result += "\nstatic "; + Write__prop_list_t_TypeDecl(Result, Properties.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_prop_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Properties.size()); Result += ",\n"; + for (unsigned i = 0, e = Properties.size(); i < e; i++) { + ObjCPropertyDecl *PropDecl = Properties[i]; + if (i == 0) + Result += "\t{{\""; + else + Result += "\t{\""; + Result += PropDecl->getName(); Result += "\","; + std::string PropertyTypeString, QuotePropertyTypeString; + Context->getObjCEncodingForPropertyDecl(PropDecl, Container, PropertyTypeString); + RewriteObj.QuoteDoublequotes(PropertyTypeString, QuotePropertyTypeString); + Result += "\""; Result += QuotePropertyTypeString; Result += "\""; + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +// Metadata flags +enum MetaDataDlags { + CLS = 0x0, + CLS_META = 0x1, + CLS_ROOT = 0x2, + OBJC2_CLS_HIDDEN = 0x10, + CLS_EXCEPTION = 0x20, + + /// (Obsolete) ARC-specific: this class has a .release_ivars method + CLS_HAS_IVAR_RELEASER = 0x40, + /// class was compiled with -fobjc-arr + CLS_COMPILED_BY_ARC = 0x80 // (1<<7) +}; + +static void Write__class_ro_t_initializer(ASTContext *Context, std::string &Result, + unsigned int flags, + const std::string &InstanceStart, + const std::string &InstanceSize, + ArrayRef<ObjCMethodDecl *>baseMethods, + ArrayRef<ObjCProtocolDecl *>baseProtocols, + ArrayRef<ObjCIvarDecl *>ivars, + ArrayRef<ObjCPropertyDecl *>Properties, + StringRef VarName, + StringRef ClassName) { + Result += "\nstatic struct _class_ro_t "; + Result += VarName; Result += ClassName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; + Result += llvm::utostr(flags); Result += ", "; + Result += InstanceStart; Result += ", "; + Result += InstanceSize; Result += ", \n"; + Result += "\t"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + // uint32_t const reserved; // only when building for 64bit targets + Result += "(unsigned int)0, \n\t"; + // const uint8_t * const ivarLayout; + Result += "0, \n\t"; + Result += "\""; Result += ClassName; Result += "\",\n\t"; + bool metaclass = ((flags & CLS_META) != 0); + if (baseMethods.size() > 0) { + Result += "(const struct _method_list_t *)&"; + if (metaclass) + Result += "_OBJC_$_CLASS_METHODS_"; + else + Result += "_OBJC_$_INSTANCE_METHODS_"; + Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && baseProtocols.size() > 0) { + Result += "(const struct _objc_protocol_list *)&"; + Result += "_OBJC_CLASS_PROTOCOLS_$_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && ivars.size() > 0) { + Result += "(const struct _ivar_list_t *)&"; + Result += "_OBJC_$_INSTANCE_VARIABLES_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + // weakIvarLayout + Result += "0, \n\t"; + if (!metaclass && Properties.size() > 0) { + Result += "(const struct _prop_list_t *)&"; + Result += "_OBJC_$_PROP_LIST_"; Result += ClassName; + Result += ",\n"; + } + else + Result += "0, \n"; + + Result += "};\n"; +} + +static void Write_class_t(ASTContext *Context, std::string &Result, + StringRef VarName, + const ObjCInterfaceDecl *CDecl, bool metaclass) { + bool rootClass = (!CDecl->getSuperClass()); + const ObjCInterfaceDecl *RootClass = CDecl; + + if (!rootClass) { + // Find the Root class + RootClass = CDecl->getSuperClass(); + while (RootClass->getSuperClass()) { + RootClass = RootClass->getSuperClass(); + } + } + + if (metaclass && rootClass) { + // Need to handle a case of use of forward declaration. + Result += "\n"; + Result += "extern \"C\" "; + if (CDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t OBJC_CLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ";\n"; + } + // Also, for possibility of 'super' metadata class not having been defined yet. + if (!rootClass) { + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + Result += "\n"; + Result += "extern \"C\" "; + if (SuperClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += SuperClass->getNameAsString(); + Result += ";\n"; + + if (metaclass && RootClass != SuperClass) { + Result += "extern \"C\" "; + if (RootClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += RootClass->getNameAsString(); + Result += ";\n"; + } + } + + Result += "\nextern \"C\" __declspec(dllexport) struct _class_t "; + Result += VarName; Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_data\"))) = {\n"; + Result += "\t"; + if (metaclass) { + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += RootClass->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else { + Result += "0, // &"; Result += VarName; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &OBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ",\n\t"; + } + } + else { + Result += "0, // &OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else + Result += "0,\n\t"; + } + Result += "0, // (void *)&_objc_empty_cache,\n\t"; + Result += "0, // unused, was (void *)&_objc_empty_vtable,\n\t"; + if (metaclass) + Result += "&_OBJC_METACLASS_RO_$_"; + else + Result += "&_OBJC_CLASS_RO_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n};\n"; + + // Add static function to initialize some of the meta-data fields. + // avoid doing it twice. + if (metaclass) + return; + + const ObjCInterfaceDecl *SuperClass = + rootClass ? CDecl : CDecl->getSuperClass(); + + Result += "static void OBJC_CLASS_SETUP_$_"; + Result += CDecl->getNameAsString(); + Result += "(void ) {\n"; + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += RootClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; + if (rootClass) + Result += "&OBJC_CLASS_$_"; + else + Result += "&OBJC_METACLASS_$_"; + + Result += SuperClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); Result += ";\n"; + + if (!rootClass) { + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; Result += "&OBJC_CLASS_$_"; + Result += SuperClass->getNameAsString(); Result += ";\n"; + } + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + Result += "}\n"; +} + +static void Write_category_t(RewriteModernObjC &RewriteObj, ASTContext *Context, + std::string &Result, + ObjCCategoryDecl *CatDecl, + ObjCInterfaceDecl *ClassDecl, + ArrayRef<ObjCMethodDecl *> InstanceMethods, + ArrayRef<ObjCMethodDecl *> ClassMethods, + ArrayRef<ObjCProtocolDecl *> RefedProtocols, + ArrayRef<ObjCPropertyDecl *> ClassProperties) { + StringRef CatName = CatDecl->getName(); + StringRef ClassName = ClassDecl->getName(); + // must declare an extern class object in case this class is not implemented + // in this TU. + Result += "\n"; + Result += "extern \"C\" "; + if (ClassDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n"; + + Result += "\nstatic struct _category_t "; + Result += "_OBJC_$_CATEGORY_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + Result += "\t\""; Result += ClassName; Result += "\",\n"; + Result += "\t0, // &"; Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ",\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_INSTANCE_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_CLASS_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (RefedProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; + Result += "_OBJC_CATEGORY_PROTOCOLS_$_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&"; Result += "_OBJC_$_PROP_LIST_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "};\n"; + + // Add static function to initialize the class pointer in the category structure. + Result += "static void OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += "(void ) {\n"; + Result += "\t_OBJC_$_CATEGORY_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += ".cls = "; Result += "&OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n}\n"; +} + +static void Write__extendedMethodTypes_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCMethodDecl *> Methods, + StringRef VarName, + StringRef ProtocolName) { + if (Methods.size() == 0) + return; + + Result += "\nstatic const char *"; + Result += VarName; Result += ProtocolName; + Result += " [] __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + std::string MethodTypeString, QuoteMethodTypeString; + Context->getObjCEncodingForMethodDecl(MD, MethodTypeString, true); + RewriteObj.QuoteDoublequotes(MethodTypeString, QuoteMethodTypeString); + Result += "\t\""; Result += QuoteMethodTypeString; Result += "\""; + if (i == e-1) + Result += "\n};\n"; + else { + Result += ",\n"; + } + } +} + +static void Write_IvarOffsetVar(RewriteModernObjC &RewriteObj, + ASTContext *Context, + std::string &Result, + ArrayRef<ObjCIvarDecl *> Ivars, + ObjCInterfaceDecl *CDecl) { + // FIXME. visibilty of offset symbols may have to be set; for Darwin + // this is what happens: + /** + if (Ivar->getAccessControl() == ObjCIvarDecl::Private || + Ivar->getAccessControl() == ObjCIvarDecl::Package || + Class->getVisibility() == HiddenVisibility) + Visibility shoud be: HiddenVisibility; + else + Visibility shoud be: DefaultVisibility; + */ + + Result += "\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (Context->getLangOpts().MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + + if (!Context->getLangOpts().MicrosoftExt || + IvarDecl->getAccessControl() == ObjCIvarDecl::Private || + IvarDecl->getAccessControl() == ObjCIvarDecl::Package) + Result += "extern \"C\" unsigned long int "; + else + Result += "extern \"C\" __declspec(dllexport) unsigned long int "; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += " __attribute__ ((used, section (\"__DATA,__objc_ivar\")))"; + Result += " = "; + RewriteObj.RewriteIvarOffsetComputation(IvarDecl, Result); + Result += ";\n"; + if (Ivars[i]->isBitField()) { + // skip over rest of the ivar bitfields. + SKIP_BITFIELDS(i , e, Ivars); + } + } +} + +static void Write__ivar_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCIvarDecl *> OriginalIvars, + StringRef VarName, + ObjCInterfaceDecl *CDecl) { + if (OriginalIvars.size() > 0) { + Write_IvarOffsetVar(RewriteObj, Context, Result, OriginalIvars, CDecl); + SmallVector<ObjCIvarDecl *, 8> Ivars; + // strip off all but the first ivar bitfield from each group of ivars. + // Such ivars in the ivar list table will be replaced by their grouping struct + // 'ivar'. + for (unsigned i = 0, e = OriginalIvars.size(); i < e; i++) { + if (OriginalIvars[i]->isBitField()) { + Ivars.push_back(OriginalIvars[i]); + // skip over rest of the ivar bitfields. + SKIP_BITFIELDS(i , e, OriginalIvars); + } + else + Ivars.push_back(OriginalIvars[i]); + } + + Result += "\nstatic "; + Write__ivar_list_t_TypeDecl(Result, Ivars.size()); + Result += " "; Result += VarName; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_ivar_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Ivars.size()); Result += ",\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (i == 0) + Result += "\t{{"; + else + Result += "\t {"; + Result += "(unsigned long int *)&"; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ", "; + + Result += "\""; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupDecl(Ivars[i], Result); + else + Result += IvarDecl->getName(); + Result += "\", "; + + QualType IVQT = IvarDecl->getType(); + if (IvarDecl->isBitField()) + IVQT = RewriteObj.GetGroupRecordTypeForObjCIvarBitfield(IvarDecl); + + std::string IvarTypeString, QuoteIvarTypeString; + Context->getObjCEncodingForType(IVQT, IvarTypeString, + IvarDecl); + RewriteObj.QuoteDoublequotes(IvarTypeString, QuoteIvarTypeString); + Result += "\""; Result += QuoteIvarTypeString; Result += "\", "; + + // FIXME. this alignment represents the host alignment and need be changed to + // represent the target alignment. + unsigned Align = Context->getTypeAlign(IVQT)/8; + Align = llvm::Log2_32(Align); + Result += llvm::utostr(Align); Result += ", "; + CharUnits Size = Context->getTypeSizeInChars(IVQT); + Result += llvm::utostr(Size.getQuantity()); + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, + std::string &Result) { + + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) + return; + WriteModernMetadataDeclarations(Context, Result); + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + for (auto *I : PDecl->protocols()) + RewriteObjCProtocolMetaData(I, Result); + + // Construct method lists. + std::vector<ObjCMethodDecl *> InstanceMethods, ClassMethods; + std::vector<ObjCMethodDecl *> OptInstanceMethods, OptClassMethods; + for (auto *MD : PDecl->instance_methods()) { + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptInstanceMethods.push_back(MD); + } else { + InstanceMethods.push_back(MD); + } + } + + for (auto *MD : PDecl->class_methods()) { + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptClassMethods.push_back(MD); + } else { + ClassMethods.push_back(MD); + } + } + std::vector<ObjCMethodDecl *> AllMethods; + for (unsigned i = 0, e = InstanceMethods.size(); i < e; i++) + AllMethods.push_back(InstanceMethods[i]); + for (unsigned i = 0, e = ClassMethods.size(); i < e; i++) + AllMethods.push_back(ClassMethods[i]); + for (unsigned i = 0, e = OptInstanceMethods.size(); i < e; i++) + AllMethods.push_back(OptInstanceMethods[i]); + for (unsigned i = 0, e = OptClassMethods.size(); i < e; i++) + AllMethods.push_back(OptClassMethods[i]); + + Write__extendedMethodTypes_initializer(*this, Context, Result, + AllMethods, + "_OBJC_PROTOCOL_METHOD_TYPES_", + PDecl->getNameAsString()); + // Protocol's super protocol list + SmallVector<ObjCProtocolDecl *, 8> SuperProtocols(PDecl->protocols()); + Write_protocol_list_initializer(Context, Result, SuperProtocols, + "_OBJC_PROTOCOL_REFS_", + PDecl->getNameAsString()); + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_PROTOCOL_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_PROTOCOL_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptInstanceMethods, + "_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptClassMethods, + "_OBJC_PROTOCOL_OPT_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ProtocolProperties(PDecl->properties()); + Write_prop_list_t_initializer(*this, Context, Result, ProtocolProperties, + /* Container */nullptr, + "_OBJC_PROTOCOL_PROPERTIES_", + PDecl->getNameAsString()); + + // Writer out root metadata for current protocol: struct _protocol_t + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__datacoal_nt,coalesced\"))) = {\n"; + Result += "\t0,\n"; // id is; is null + Result += "\t\""; Result += PDecl->getNameAsString(); Result += "\",\n"; + if (SuperProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; Result += "_OBJC_PROTOCOL_REFS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptInstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ProtocolProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&_OBJC_PROTOCOL_PROPERTIES_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "\t"; Result += "sizeof(_protocol_t)"; Result += ",\n"; + Result += "\t0,\n"; + + if (AllMethods.size() > 0) { + Result += "\t(const char **)&"; Result += "_OBJC_PROTOCOL_METHOD_TYPES_"; + Result += PDecl->getNameAsString(); + Result += "\n};\n"; + } + else + Result += "\t0\n};\n"; + + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_LABEL_PROTOCOL_$_"; Result += PDecl->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl()).second) + llvm_unreachable("protocol already synthesized"); + +} + +void RewriteModernObjC::RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Protocols, + StringRef prefix, StringRef ClassName, + std::string &Result) { + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) + RewriteObjCProtocolMetaData(Protocols[i], Result); + + // 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 += "\n"; + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".cat_cls_meth$B\")) "; + Result += "static 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"; +} + +/// hasObjCExceptionAttribute - Return true if this class or any super +/// class has the __objc_exception__ attribute. +/// FIXME. Move this to ASTContext.cpp as it is also used for IRGen. +static bool hasObjCExceptionAttribute(ASTContext &Context, + const ObjCInterfaceDecl *OID) { + if (OID->hasAttr<ObjCExceptionAttr>()) + return true; + if (const ObjCInterfaceDecl *Super = OID->getSuperClass()) + return hasObjCExceptionAttribute(Context, Super); + return false; +} + +void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + + WriteModernMetadataDeclarations(Context, Result); + SmallVector<ObjCIvarDecl *, 8> IVars; + + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + // Ignore unnamed bit-fields. + if (!IVD->getDeclName()) + continue; + IVars.push_back(IVD); + } + + Write__ivar_list_t_initializer(*this, Context, Result, IVars, + "_OBJC_$_INSTANCE_VARIABLES_", + CDecl); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_INSTANCE_METHODS_", + IDecl->getNameAsString(), true); + + SmallVector<ObjCMethodDecl *, 32> ClassMethods(IDecl->class_methods()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CLASS_METHODS_", + IDecl->getNameAsString(), true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + std::vector<ObjCProtocolDecl *> RefedProtocols; + const ObjCList<ObjCProtocolDecl> &Protocols = CDecl->getReferencedProtocols(); + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), + E = Protocols.end(); + I != E; ++I) { + RefedProtocols.push_back(*I); + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(*I, Result); + } + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CLASS_PROTOCOLS_$_", + IDecl->getNameAsString()); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ClassProperties(CDecl->properties()); + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + CDecl->getNameAsString()); + + + // Data for initializing _class_ro_t metaclass meta-data + uint32_t flags = CLS_META; + std::string InstanceSize; + std::string InstanceStart; + + + bool classIsHidden = CDecl->getVisibility() == HiddenVisibility; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + InstanceSize = "sizeof(struct _class_t)"; + InstanceStart = InstanceSize; + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + ClassMethods, + nullptr, + nullptr, + nullptr, + "_OBJC_METACLASS_RO_$_", + CDecl->getNameAsString()); + + // Data for initializing _class_ro_t meta-data + flags = CLS; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (hasObjCExceptionAttribute(*Context, CDecl)) + flags |= CLS_EXCEPTION; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + + InstanceSize.clear(); + InstanceStart.clear(); + if (!ObjCSynthesizedStructs.count(CDecl)) { + InstanceSize = "0"; + InstanceStart = "0"; + } + else { + InstanceSize = "sizeof(struct "; + InstanceSize += CDecl->getNameAsString(); + InstanceSize += "_IMPL)"; + + ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + if (IVD) { + RewriteIvarOffsetComputation(IVD, InstanceStart); + } + else + InstanceStart = InstanceSize; + } + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + InstanceMethods, + RefedProtocols, + IVars, + ClassProperties, + "_OBJC_CLASS_RO_$_", + CDecl->getNameAsString()); + + Write_class_t(Context, Result, + "OBJC_METACLASS_$_", + CDecl, /*metaclass*/true); + + Write_class_t(Context, Result, + "OBJC_CLASS_$_", + CDecl, /*metaclass*/false); + + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyClasses.push_back(CDecl); + +} + +void RewriteModernObjC::RewriteClassSetupInitHook(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + if (!ClsDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CLASS_SETUP[] = {\n"; + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *IDecl = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CLASS_SETUP_$_"; + Result += CDecl->getName(); Result += ",\n"; + } + Result += "};\n"; +} + +void RewriteModernObjC::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + RewriteClassSetupInitHook(Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + RewriteCategorySetupInitHook(Result); + + if (ClsDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_classlist$B\")) "; + Result += "static struct _class_t *L_OBJC_LABEL_CLASS_$ ["; + Result += llvm::utostr(ClsDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_classlist," + "regular,no_dead_strip\")))= {\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t&OBJC_CLASS_$_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + + if (!DefinedNonLazyClasses.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlclslist$B\")) \n"; + Result += "static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyClasses.size(); i < e; i++) { + Result += "\t&OBJC_CLASS_$_"; Result += DefinedNonLazyClasses[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } + } + + if (CatDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_catlist$B\")) "; + Result += "static struct _category_t *L_OBJC_LABEL_CATEGORY_$ ["; + Result += llvm::utostr(CatDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_catlist," + "regular,no_dead_strip\")))= {\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"; + } + + if (!DefinedNonLazyCategories.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlcatlist$B\")) \n"; + Result += "static struct _category_t *_OBJC_LABEL_NONLAZY_CATEGORY_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyCategories.size(); i < e; i++) { + Result += "\t&_OBJC_$_CATEGORY_"; + Result += + DefinedNonLazyCategories[i]->getClassInterface()->getNameAsString(); + Result += "_$_"; + Result += DefinedNonLazyCategories[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } +} + +void RewriteModernObjC::WriteImageInfo(std::string &Result) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_imageinfo$B\")) \n"; + + Result += "static struct IMAGE_INFO { unsigned version; unsigned flag; } "; + // version 0, ObjCABI is 2 + Result += "_OBJC_IMAGE_INFO = { 0, 2 };\n"; +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + WriteModernMetadataDeclarations(Context, Result); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl + = ClassDecl->FindCategoryDeclaration(IDecl->getIdentifier()); + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += "_$_"; + FullCategoryName += CDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_CATEGORY_INSTANCE_METHODS_", + FullCategoryName, true); + + SmallVector<ObjCMethodDecl *, 32> ClassMethods(IDecl->class_methods()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CATEGORY_CLASS_METHODS_", + FullCategoryName, true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + SmallVector<ObjCProtocolDecl *, 8> RefedProtocols(CDecl->protocols()); + for (auto *I : CDecl->protocols()) + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(I, Result); + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CATEGORY_PROTOCOLS_$_", + FullCategoryName); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ClassProperties(CDecl->properties()); + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + FullCategoryName); + + Write_category_t(*this, Context, Result, + CDecl, + ClassDecl, + InstanceMethods, + ClassMethods, + RefedProtocols, + ClassProperties); + + // Determine if this category is also "non-lazy". + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyCategories.push_back(CDecl); + +} + +void RewriteModernObjC::RewriteCategorySetupInitHook(std::string &Result) { + int CatDefCount = CategoryImplementation.size(); + if (!CatDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CATEGORY_SETUP[] = {\n"; + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *IDecl = CategoryImplementation[i]; + ObjCCategoryDecl *CatDecl= IDecl->getCategoryDecl(); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getName(); + Result += "_$_"; + Result += CatDecl->getName(); + Result += ",\n"; + } + Result += "};\n"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template<typename MethodIterator> +void RewriteModernObjC::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + 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[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + Result += "\n"; + if (LangOpts.MicrosoftExt) { + if (IsInstanceMethod) + Result += "__declspec(allocate(\".inst_meth$B\")) "; + else + Result += "__declspec(allocate(\".cls_meth$B\")) "; + } + Result += "static struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(NumMethods); + 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(NumMethods) + "\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"; +} + +Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + if (D->isBitField()) + ObjCIvarBitfieldGroupOffset(D, IvarOffsetName); + else + WriteInternalIvarName(clsDeclared, D, IvarOffsetName); + + ReferencedIvars[clsDeclared].insert(D); + + // cast offset to "char *". + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->CharTy), + CK_BitCast, + BaseExpr); + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(IvarOffsetName), + Context->UnsignedLongTy, nullptr, + SC_Extern); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, + Context->UnsignedLongTy, VK_LValue, + SourceLocation()); + BinaryOperator *addExpr = + new (Context) BinaryOperator(castExpr, DRE, BO_Add, + Context->getPointerType(Context->CharTy), + VK_RValue, OK_Ordinary, SourceLocation(), false); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), + SourceLocation(), + addExpr); + QualType IvarT = D->getType(); + if (D->isBitField()) + IvarT = GetGroupRecordTypeForObjCIvarBitfield(D); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + + castExpr = NoTypeInfoCStyleCastExpr(Context, + castT, + CK_BitCast, + PE); + + + Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, + VK_LValue, OK_Ordinary, + SourceLocation()); + PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + Exp); + + if (D->isBitField()) { + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + D->getType(), nullptr, + /*BitWidth=*/D->getBitWidth(), + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, /*isArrow*/false, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + Replacement = ME; + + } + else + Replacement = PE; + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} + +#endif diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp new file mode 100644 index 0000000..5196810 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -0,0 +1,5932 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +#ifdef CLANG_ENABLE_OBJC_REWRITER + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteObjC : public ASTConsumer { + protected: + + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + static const int OBJC_ABI_VERSION = 7; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + raw_ostream* OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + unsigned RewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperConstructorFunctionDecl; + FunctionDecl *CurFunctionDef; + FunctionDecl *CurFunctionDeclToDeclareForBlock; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; + 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; + SmallVector<Stmt *, 32> Stmts; + SmallVector<int, 8> ObjCBcLabelNo; + // Remember all the @protocol(<expr>) expressions. + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ProtocolExprDecls; + + llvm::DenseSet<uint64_t> CopyDestroyCache; + + // Block expressions. + SmallVector<BlockExpr *, 32> Blocks; + SmallVector<int, 32> InnerDeclRefsCount; + SmallVector<DeclRefExpr *, 32> InnerDeclRefs; + + SmallVector<DeclRefExpr *, 32> BlockDeclRefs; + + // Block related declarations. + SmallVector<ValueDecl *, 8> BlockByCopyDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDeclsPtrSet; + SmallVector<ValueDecl *, 8> BlockByRefDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDeclsPtrSet; + llvm::DenseMap<ValueDecl *, unsigned> BlockByRefDeclNo; + llvm::SmallPtrSet<ValueDecl *, 8> ImportedBlockDecls; + llvm::SmallPtrSet<VarDecl *, 8> ImportedLocalExternalDecls; + + llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; + + // 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; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + void InitializeCommon(ASTContext &context); + + public: + + // Top Level Driver code. + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + HandleTopLevelSingleDecl(*I); + } + return true; + } + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteObjC(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn); + + ~RewriteObjC() {} + + void HandleTranslationUnit(ASTContext &C) override; + + void ReplaceStmt(Stmt *Old, Stmt *New) { + ReplaceStmtWithRange(Old, New, Old->getSourceRange()); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + assert(Old != nullptr && New != nullptr && "Expected non-null Stmt's"); + + Stmt *ReplacingStmt = ReplacedNodes[Old]; + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // Measure 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, nullptr, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + void RewriteTryReturnStmts(Stmt *S); + void RewriteSyncReturnStmts(Stmt *S, std::string buf); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void Initialize(ASTContext &context) override = 0; + + // Metadata Rewriting. + virtual void RewriteMetaDataIntoBuffer(std::string &Result) = 0; + virtual void RewriteObjCProtocolListMetaData(const ObjCList<ObjCProtocolDecl> &Prots, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result) = 0; + virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) = 0; + + // Rewriting ivar access + virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) = 0; + virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) = 0; + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + Expr **args, unsigned nargs, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperConstructorFunctionDecl(); + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void HasReturnStmts(Stmt *S, bool &hasReturns); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts); + + // 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); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs<BlockPointerType>(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) + T = Context->getObjCIdType(); + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(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->getAs<PointerType>()) { + if (isa<ObjCInterfaceType>(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + ArrayRef<QualType> args, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, nullptr, + TInfo, SourceLocation(), SourceLocation()); + } + + StringLiteral *getStringLiteral(StringRef Str) { + QualType StrType = Context->getConstantArrayType( + Context->CharTy, llvm::APInt(32, Str.size() + 1), ArrayType::Normal, + 0); + return StringLiteral::Create(*Context, Str, StringLiteral::Ascii, + /*Pascal=*/false, StrType, SourceLocation()); + } + }; + + class RewriteObjCFragileABI : public RewriteObjC { + public: + + RewriteObjCFragileABI(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) : RewriteObjC(inFile, OS, + D, LOpts, + silenceMacroWarn) {} + + ~RewriteObjCFragileABI() {} + void Initialize(ASTContext &context) override; + + // Rewriting metadata + template<typename MethodIterator> + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, StringRef ClassName, + std::string &Result) override; + void RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Prots, + StringRef prefix, StringRef ClassName, std::string &Result) override; + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) override; + void RewriteMetaDataIntoBuffer(std::string &Result) override; + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result) override; + + // Rewriting ivar + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) override; + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) override; + }; +} + +void RewriteObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast<FunctionProtoType>(funcType.IgnoreParens())) { + for (const auto &I : fproto->param_types()) + 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->getAs<PointerType>(); + 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, raw_ostream* OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), + SilenceRewriteMacroWarning(silenceMacroWarn) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +std::unique_ptr<ASTConsumer> +clang::CreateObjCRewriter(const std::string &InFile, raw_ostream *OS, + DiagnosticsEngine &Diags, const LangOptions &LOpts, + bool SilenceRewriteMacroWarning) { + return llvm::make_unique<RewriteObjCFragileABI>(InFile, OS, Diags, LOpts, + SilenceRewriteMacroWarning); +} + +void RewriteObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = nullptr; + MsgSendSuperFunctionDecl = nullptr; + MsgSendStretFunctionDecl = nullptr; + MsgSendSuperStretFunctionDecl = nullptr; + MsgSendFpretFunctionDecl = nullptr; + GetClassFunctionDecl = nullptr; + GetMetaClassFunctionDecl = nullptr; + GetSuperClassFunctionDecl = nullptr; + SelGetUidFunctionDecl = nullptr; + CFStringFunctionDecl = nullptr; + ConstantStringClassReference = nullptr; + NSStringRecord = nullptr; + CurMethodDef = nullptr; + CurFunctionDef = nullptr; + CurFunctionDeclToDeclareForBlock = nullptr; + GlobalVarDecl = nullptr; + SuperStructDecl = nullptr; + ProtocolTypeDecl = nullptr; + ConstantStringDecl = nullptr; + BcLabelCount = 0; + SuperConstructorFunctionDecl = nullptr; + NumObjCStringLiterals = 0; + PropParentMap = nullptr; + CurrentBody = nullptr; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // 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->getExpansionLoc(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 (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) { + if (ID->isThisDeclarationADefinition()) + RewriteInterfaceDecl(ID); + } else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } 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; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = IFace->getLocStart(); + do { + if (isa<ObjCInterfaceDecl>(*DI) && + !cast<ObjCInterfaceDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = Proto->getLocStart(); + do { + if (isa<ObjCProtocolDecl>(*DI) && + !cast<ObjCProtocolDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isWrittenInMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // 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.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static std::string getIvarAccessString(ObjCIvarDecl *OID) { + const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface(); + std::string S; + S = "((struct "; + S += ClassDecl->getIdentifier()->getName(); + S += "_IMPL *)self)->"; + S += OID->getName(); + return S; +} + +void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// "); + 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.getLocWithOffset(semiBuf-startBuf+1); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + + if (!OID) + return; + unsigned Attributes = PD->getPropertyAttributes(); + if (!PD->getGetterMethodDecl()->isDefined()) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = nullptr; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(onePastSemiLoc, Getr); + } + + if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }"; + InsertText(onePastSemiLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + 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"; +} + +void RewriteObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(*I); + if (I == D.begin()) { + // 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. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(*I), typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(D[0]), typedefString); +} + +void RewriteObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // 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, "// "); + + for (auto *I : CatDecl->properties()) + RewriteProperty(I); + for (auto *I : CatDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : CatDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */"); +} + +void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getLocStart(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (auto *I : PDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->class_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->properties()) + RewriteProperty(I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */"); + + // 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"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteObjC::RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG) { + SourceLocation LocStart = DG[0]->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void RewriteObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs<PointerType>()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs<BlockPointerType>()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs<FunctionType>())) { + ResultStr += + FPRetType->getReturnType().getAsString(Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = nullptr; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getReturnType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->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(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast<ObjCInterfaceDecl*>(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (const auto *PDecl : OMD->params()) { + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + 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->getNumParams(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} +void RewriteObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast<ObjCImplementationDecl>(OID); + ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); + + InsertText(IMD ? IMD->getLocStart() : CID->getLocStart(), "// "); + + for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (auto *I : IMD ? IMD->property_impls() : CID->property_impls()) + RewritePropertyImplDecl(I, IMD, CID); + + InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); +} + +void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + std::string ResultStr; + if (!ObjCForwardDecls.count(ClassDecl->getCanonicalDecl())) { + // 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->getCanonicalDecl()); + } + RewriteObjCInternalStruct(ClassDecl, ResultStr); + + for (auto *I : ClassDecl->properties()) + RewriteProperty(I); + for (auto *I : ClassDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : ClassDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */"); +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base, *RHS; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = nullptr; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + // Rebuild the RHS. + RHS = cast<BinaryOperator>(PseudoOp->getSyntacticForm())->getRHS(); + RHS = cast<OpaqueValueExpr>(RHS)->getSourceExpr(); + RHS = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(RHS)); + } + + // TODO: avoid this copy. + SmallVector<SourceLocation, 1> SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = nullptr; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + } + + // Intentionally empty. + SmallVector<SourceLocation, 1> SelLocs; + SmallVector<Expr*, 1> Args; + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// 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 *)__rw_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 *)__rw_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); + + return nullptr; +} + +/// 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); + + return nullptr; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_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:__rw_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); + StringRef 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->getSingleDecl()); + QualType ElementType = cast<ValueDecl>(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast<DeclRefExpr>(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = cast<ValueDecl>(DR->getDecl()); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_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); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_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 *)__rw_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); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while (limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_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 += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa<CompoundStmt>(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } 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.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return nullptr; +} + +/// 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); + // 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.getLocWithOffset(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); + 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 += "}\n"; + buf += "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + + std::string syncBuf; + syncBuf += " objc_sync_exit("; + + Expr *syncExpr = S->getSynchExpr(); + CastKind CK = syncExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : + syncExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + syncExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, syncExpr); + std::string syncExprBufS; + llvm::raw_string_ostream syncExprBuf(syncExprBufS); + assert(syncExpr != nullptr && "Expected non-null Expr"); + syncExpr->printPretty(syncExprBuf, nullptr, PrintingPolicy(LangOpts)); + syncBuf += syncExprBuf.str(); + syncBuf += ");"; + + buf += syncBuf; + buf += "\n if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}\n"; + buf += "}"; + + ReplaceText(lastCurlyLoc, 1, buf); + + bool hasReturns = false; + HasReturnStmts(S->getSynchBody(), hasReturns); + if (hasReturns) + RewriteSyncReturnStmts(S->getSynchBody(), syncBuf); + + return nullptr; +} + +void RewriteObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + WarnAboutReturnGotoStmts(*CI); + + if (isa<ReturnStmt>(S) || isa<GotoStmt>(S)) { + Diags.Report(Context->getFullLoc(S->getLocStart()), + TryFinallyContainsReturnDiag); + } + return; +} + +void RewriteObjC::HasReturnStmts(Stmt *S, bool &hasReturns) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + HasReturnStmts(*CI, hasReturns); + + if (isa<ReturnStmt>(S)) + hasReturns = true; + return; +} + +void RewriteObjC::RewriteTryReturnStmts(Stmt *S) { + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + RewriteTryReturnStmts(*CI); + } + if (isa<ReturnStmt>(S)) { + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteTryReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack); return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } + return; +} + +void RewriteObjC::RewriteSyncReturnStmts(Stmt *S, std::string syncExitBuf) { + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + RewriteSyncReturnStmts(*CI, syncExitBuf); + } + if (isa<ReturnStmt>(S)) { + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteSyncReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack);"; + buf += syncExitBuf; + buf += " return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } + 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); + + startLoc = S->getTryBody()->getLocEnd(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @try block"); + + SourceLocation lastCurlyLoc = startLoc; + if (S->getNumCatchStmts()) { + startLoc = startLoc.getLocWithOffset(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); + } else { /* no catch list */ + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf); + } + Stmt *lastCatchBody = nullptr; + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + if (I == 0) + buf = "if ("; // we are generating code for the first catch clause + else + buf = "else if ("; + startLoc = Catch->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @catch location"); + + const char *lParenLoc = strchr(startBuf, '('); + + if (Catch->hasEllipsis()) { + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation bodyLoc = lastCatchBody->getLocStart(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + assert(*SM->getCharacterData(Catch->getRParenLoc()) == ')' && + "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + buf += "1) { id _tmp = _caught;"; + Rewrite.ReplaceText(startLoc, bodyBuf-startBuf+1, buf); + } else if (catchDecl) { + QualType t = catchDecl->getType(); + if (t == Context->getObjCIdType()) { + buf += "1) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } else if (const ObjCObjectPointerType *Ptr = + t->getAs<ObjCObjectPointerType>()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + buf += "objc_exception_match((struct objc_class *)objc_getClass(\""; + buf += IDecl->getNameAsString(); + buf += "\"), (struct objc_object *)_caught)) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } + } + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation rParenLoc = Catch->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"); + + // Here we replace ") {" with "= _caught;" (which initializes and + // declares the @catch parameter). + ReplaceText(rParenLoc, bodyBuf-rParenBuf+1, " = _caught;"); + } else { + llvm_unreachable("@catch rewrite bug"); + } + } + // 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.getLocWithOffset(-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); + + // Set lastCurlyLoc + lastCurlyLoc = lastCatchBody->getLocEnd(); + } + if (ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt()) { + startLoc = finalStmt->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @finally start"); + + ReplaceText(startLoc, 8, "/* @finally */"); + + 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.getLocWithOffset(1); + InsertText(startLoc, " if (!_rethrow) objc_exception_try_exit(&_stack);\n"); + endLoc = endLoc.getLocWithOffset(-1); + InsertText(endLoc, " if (_rethrow) objc_exception_throw(_rethrow);\n"); + + // Set lastCurlyLoc + lastCurlyLoc = body->getLocEnd(); + + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(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); + + // Now check for any return/continue/go statements within the @try. + // The implicit finally clause won't called if the @try contains any + // jump statements. + bool hasReturns = false; + HasReturnStmts(S->getTryBody(), hasReturns); + if (hasReturns) + RewriteTryReturnStmts(S->getTryBody()); + } + // Now emit the final closing curly brace... + lastCurlyLoc = lastCurlyLoc.getLocWithOffset(1); + InsertText(lastCurlyLoc, " } /* @try scope end */\n"); + return nullptr; +} + +// 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); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + ReplaceText(semiLoc, 1, ");"); + return nullptr; +} + +Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = getStringLiteral(StrEncoding); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() 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"). + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( + FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, + SourceLocation EndLoc) { + // 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, false, msgSendType, + VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, nullptr, VK_RValue); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + + CallExpr *Exp = + new (Context) CallExpr(*Context, ICE, llvm::makeArrayRef(args, nargs), + FT->getCallResultType(*Context), + VK_RValue, EndLoc); + return Exp; +} + +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 *PT = T->getAs<PointerType>()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + 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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = nullptr; + 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()->getAs<FunctionType>(); + assert(funcType && "missing function type"); + proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + Type = proto->getReturnType(); + } + else if (FieldDecl *FD = dyn_cast<FieldDecl>(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + 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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + 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->getNumParams(); i++) { + if (needToScanForQualifiers(proto->getParamType(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 = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + 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++; + } + } +} + +void RewriteObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (!isa<TypeOfExprType>(TypePtr)) + return; + while (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs<Type>(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), ArgTys); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, + nullptr, SC_Extern); +} + +void RewriteObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in <objc/objc.h> + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + + +void RewriteObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + QualType Type = proto->getReturnType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumParams(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getParamType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); + CurFunctionDeclToDeclareForBlock = nullptr; +} + +// SynthSuperConstructorFunctionDecl - id objc_super(id obj, id super); +void RewriteObjC::SynthSuperConstructorFunctionDecl() { + if (SuperConstructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + SuperConstructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), 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 = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, + msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + 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 = getSimpleFunctionType(Context->DoubleTy, + ArgTys, /*isVariadic=*/true); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthGetClassFunctionDecl - id objc_getClass(const char *name); +void RewriteObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, nullptr, + SC_Extern); +} + +// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name); +void RewriteObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + assert(Exp != nullptr && "Expected non-null ObjCStringLiteral"); + 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 (!isAlphanumeric(c)) + 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, nullptr, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, nullptr, SC_Static); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, + SourceLocation()); + Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +// struct objc_super { struct objc_object *receiver; struct objc_class *super; }; +QualType RewriteObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), 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(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), 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(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Create a reference to the objc_msgSend_stret() declaration. + DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, + false, msgSendType, + VK_LValue, SourceLocation()); + // Need to cast objc_msgSend_stret to "void *" (see above comment). + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, STDRE); + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, ArgTypes, + Method ? Method->isVariadic() + : false); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *STCE = new (Context) CallExpr( + *Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, SourceLocation()); + return STCE; + +} + + +Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = nullptr; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getReturnType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 8> MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector<Expr*, 4> InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, + EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperConstructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, + 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, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector<Expr*, 4> InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperConstructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, 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, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo<Proto> *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, + EndLoc); + 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(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // 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(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + 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 (const auto *PI : OMD->params()) { + QualType t = PI->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : PI->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } 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, false, msgSendType, + VK_LValue, 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 = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + // If we don't have a method decl, force a variadic cast. + const ObjCMethodDecl *MD = Exp->getMethodDecl(); + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, MD ? MD->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = new (Context) + CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc); + 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. + + CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + + // Build sizeof(returnType) + UnaryExprOrTypeTraitExpr *sizeofExpr = + new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, + Context->getTrivialTypeSourceInfo(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 = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, 8), + Context->IntTy, + SourceLocation()); + BinaryOperator *lessThanExpr = + new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, + VK_RValue, OK_Ordinary, SourceLocation(), + false); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(lessThanExpr, + SourceLocation(), CE, + SourceLocation(), STCE, + returnType, VK_RValue, OK_Ordinary); + ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + CondExpr); + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), + Exp->getLocEnd()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteObjC::HandleTranslationUnit(). +Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_" + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), + nullptr, SC_Extern); + DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), + VK_LValue, SourceLocation()); + Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), + CK_BitCast, + DerefExpr); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; + +} + +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; +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "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->getEndOfDefinitionLoc(); + + 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->isThisDeclarationADefinition() || NumIvars == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // FIXME: This has potential of causing problem. If + // SynthesizeObjCInternalStruct is ever called recursively. + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + if (LangOpts.MicrosoftExt) + 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->getAtStartLoc(); + const char *endHeader = SM->getCharacterData(L); + endHeader += Lexer::MeasureTokenLength(L, *SM, LangOpts); + + 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); + } else { + // rewrite the original header *without* disturbing the '{' + ReplaceText(LocStart, cursor-startBuf, Result); + } + 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.getLocWithOffset(cursor-startBuf+1); + InsertText(OnePastCurly, Result); + } + cursor++; // past '{' + + // Now comment out any visibility specifiers. + while (cursor < endBuf) { + if (*cursor == '@') { + SourceLocation atLoc = LocStart.getLocWithOffset(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, "// "); + } + // 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.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, "/* "); + cursor = strchr(cursor, '>'); + cursor++; + atLoc = LocStart.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, " */"); + } else if (*cursor == '^') { // rewrite block specifier. + SourceLocation caretLoc = LocStart.getLocWithOffset(cursor-startBuf); + ReplaceText(caretLoc, 1, "*"); + } + cursor++; + } + // Don't forget to add a ';'!! + InsertText(LocEnd.getLocWithOffset(1), ";"); + } else { // we don't have any instance variables - insert super struct. + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + Result += " {\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n};\n"; + ReplaceText(LocStart, endBuf-startBuf, Result); + } + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl).second) + llvm_unreachable("struct already synthesize- SynthesizeObjCInternalStruct"); +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + + +/// 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::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getReturnType(); + std::string StructRef = "struct " + Tag; + std::string S = "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_" + "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(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + 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 (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // 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())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + 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, + StringRef 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 (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_assign((void*)&dst->"; + S += VD->getNameAsString(); + S += ", (void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_dispose((void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::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 { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n unsigned long reserved;\n"; + S += " unsigned long Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + // Insert declaration for the function in which block literal is used. + if (CurFunctionDeclToDeclareForBlock && !Blocks.empty()) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->hasAttr<BlocksAttr>() || + VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(":", loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + //SourceLocation FunLocStart = MD->getLocStart(); + SourceLocation FunLocStart = MD->getLocStart(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) + // FIXME: Handle enums. + BlockDeclRefs.push_back(DRE); + + return; +} + +void RewriteObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(*CI)) { + InnerContexts.insert(cast<DeclContext>(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(*CI, + InnerBlockDeclRefs, + InnerContexts); + + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = cast<VarDecl>(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } + + return; +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + QualType Res = FT->getReturnType(); + bool HasBlockType = convertBlockPointerToFunctionPointer(Res); + + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertBlockPointerToFunctionPointer(t)) + HasBlockType = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + // FIXME. Does this work if block takes no argument but has a return type + // which is of block type? + if (HasBlockType) + FuncType = getSimpleFunctionType(Res, ArgTypes); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = nullptr; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(BlockExp)) { + CPT = DRE->getType()->getAs<BlockPointerType>(); + } else if (const MemberExpr *MExpr = dyn_cast<MemberExpr>(BlockExp)) { + CPT = MExpr->getType()->getAs<BlockPointerType>(); + } + else if (const ParenExpr *PRE = dyn_cast<ParenExpr>(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast<ImplicitCastExpr>(BlockExp)) + CPT = IEXPR->getType()->getAs<BlockPointerType>(); + else if (const ConditionalOperator *CEXPR = + dyn_cast<ConditionalOperator>(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast<Expr>(LHSStmt), + SourceLocation(), cast<Expr>(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { + CPT = IRE->getType()->getAs<BlockPointerType>(); + } else if (const PseudoObjectExpr *POE + = dyn_cast<PseudoObjectExpr>(BlockExp)) { + CPT = POE->getType()->castAs<BlockPointerType>(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs<FunctionType>(); + 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, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType = getSimpleFunctionType(Exp->getType(), ArgTypes); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast<Expr*>(BlockExp)); + // 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, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + 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, + Exp->getType(), VK_RValue, + SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// BlockDeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a BlockDeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, + FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation()); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return 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); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 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.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) + if (isTopLevelBlockPointerType(I)) + return true; + } + return false; +} + +bool RewriteObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) { + if (I->isObjCQualifiedIdType()) + return true; + if (I->isObjCObjectPointerType() && + I->getPointeeType()->isObjCQualifiedInterfaceType()) + 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 (TypedefNameDecl *TDD = dyn_cast<TypedefNameDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("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--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *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. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id<P> with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); + + return; +} + + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast<unsigned>(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteObjC::RewriteByRefVar(VarDecl *ND) { + // Insert declaration for the function in which block literal is + // used. + if (CurFunctionDeclToDeclareForBlock) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty, ND); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = CurFunctionDef->getTypeSpecStartLoc(); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + InsertText(FunLocStart, ByrefType); + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + InsertText(FunLocStart, HF); + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != nullptr); + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + if (!hasInit) { + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is aleady + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ByrefType += " " + Name; + ByrefType += " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += "), "; + if (HasCopyAndDispose) { + ByrefType += "__Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + ByrefType += ", "; + } + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + // Complete the newly synthesized compound expression by inserting a right + // curly brace before the end of the declaration. + // FIXME: This approach avoids rewriting the initializer expression. It + // also assumes there is only one declarator. For example, the following + // isn't currently supported by this routine (in general): + // + // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37; + // + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *semiBuf = strchr(startInitializerBuf, ';'); + assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'"); + SourceLocation semiLoc = + startLoc.getLocWithOffset(semiBuf-startInitializerBuf); + + InsertText(semiLoc, "}"); + } + 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]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, nullptr, SC_Extern, + false, false); +} + +Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs) { + const BlockDecl *block = Exp->getBlockDecl(); + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + 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 BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a constructor call... + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, + SourceLocation()); + + SmallVector<Expr*, 4> InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData.c_str()), + Context->VoidPtrTy, nullptr, + SC_Static); + UnaryOperator *DescRefExpr = + new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, + Context->VoidPtrTy, + VK_LValue, + SourceLocation()), + UO_AddrOf, + Context->getPointerType(Context->VoidPtrTy), + VK_RValue, OK_Ordinary, + SourceLocation()); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (const auto &CI : block->captures()) { + const VarDecl *variable = CI.getVariable(); + if (variable == ND && CI.isNested()) { + assert (CI.isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = new (Context) CallExpr(*Context, DRE, InitExprs, + FType, VK_LValue, SourceLocation()); + NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast<ObjCForCollectionStmt>(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +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); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) { + if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + Stmt *childStmt = (*CI); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + *CI = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + SmallVector<DeclRefExpr *, 8> InnerBlockDeclRefs; + llvm::SmallPtrSet<const DeclContext *, 8> InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = nullptr; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = nullptr; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) + return RewriteAtEncode(AtEncode); + + 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. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id <FooProtocol> index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (auto *SD : DS->decls()) { + if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + if (VarDecl *VD = dyn_cast<VarDecl>(SD)) { + if (VD->hasAttr<BlocksAttr>()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(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 (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr<BlocksAttr>()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + 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; +} + +void RewriteObjC::RewriteRecordBody(RecordDecl *RD) { + for (auto *FD : RD->fields()) { + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = 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 (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { + CurFunctionDef = FD; + CurFunctionDeclToDeclareForBlock = FD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + CurFunctionDef = nullptr; + CurFunctionDeclToDeclareForBlock = nullptr; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + InsertBlockLiteralsWithinMethod(MD); + CurMethodDef = nullptr; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast<ObjCImplementationDecl>(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast<ObjCCategoryImplDecl>(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = 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); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = nullptr; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast<RecordDecl>(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +void RewriteObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(<expr>). + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) + RewriteObjCProtocolMetaData(ProtDecl, "", "", Preamble); + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, 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 { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteObjCFragileABI::Initialize(ASTContext &context) { + InitializeCommon(context); + + // 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.MicrosoftExt) { + // 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.MicrosoftExt) { + 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_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\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 int objc_sync_enter(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int 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 Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#define __weak\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This rutine synthesizes computation of +/// ivar offset. +void RewriteObjCFragileABI::RewriteIvarOffsetComputation(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 += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + Result += ivar->getNameAsString(); + Result += ")"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteObjCFragileABI::RewriteObjCProtocolMetaData( + ObjCProtocolDecl *PDecl, StringRef prefix, + StringRef ClassName, std::string &Result) { + static bool objc_protocol_methods = false; + + // Output struct protocol_methods holder of method selector and type. + if (!objc_protocol_methods && PDecl->hasDefinition()) { + /* struct protocol_methods { + SEL _cmd; + char *method_types; + } + */ + Result += "\nstruct _protocol_methods {\n"; + Result += "\tstruct objc_selector *_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->getCanonicalDecl())) + return; + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + + 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 protocol_methods["; + 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 ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + 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 protocol_methods["; + 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 ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + 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->getCanonicalDecl()).second) + llvm_unreachable("protocol already synthesized"); + +} + +void RewriteObjCFragileABI::RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Protocols, + StringRef prefix, StringRef ClassName, + std::string &Result) { + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) + RewriteObjCProtocolMetaData(Protocols[i], prefix, ClassName, Result); + + // 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"; +} + +void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) { + // FIXME: Implementation of a class with no @interface (legacy) does not + // produce correct synthesis as yet. + RewriteObjCInternalStruct(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; + SmallVector<ObjCIvarDecl *, 8> IVars; + if (!IDecl->ivar_empty()) { + for (auto *IV : IDecl->ivars()) + IVars.push_back(IV); + 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 TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + for (++IVI; IVI != IVE; ++IVI) { + Result += "\t ,{\""; + Result += IVI->getNameAsString(); + Result += "\", \""; + std::string TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + } + + Result += "\t }\n};\n"; + } + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (!Getter->isDefined()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (!Setter->isDefined()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), + true, "", IDecl->getName(), Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "", IDecl->getName(), Result); + + // Protocols referenced in class declaration? + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), + "CLASS", CDecl->getName(), 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 = nullptr; + 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.MicrosoftExt) + 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"; +} + +void RewriteObjCFragileABI::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // 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.MicrosoftExt) { + if (ProtocolExprDecls.size()) { + Result += "#pragma section(\".objc_protocol$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_protocol$B\")\n"; + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) { + Result += "static struct _objc_protocol *_POINTER_OBJC_PROTOCOL_"; + Result += ProtDecl->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; + Result += ProtDecl->getNameAsString(); + Result += ";\n"; + } + Result += "#pragma data_seg(pop)\n\n"; + } + 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"; + } +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl + = ClassDecl->FindCategoryDeclaration(IDecl->getIdentifier()); + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += '_'; + FullCategoryName += IDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.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) + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), "CATEGORY", + FullCategoryName, 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"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template<typename MethodIterator> +void RewriteObjCFragileABI::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + 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[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + 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(NumMethods); + 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(NumMethods) + "\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"; +} + +Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + if (CurMethodDef) { + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + 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); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + castExpr); + if (IV->isFreeIvar() && + declaresSameEntity(CurMethodDef->getClassInterface(), iFaceDecl->getDecl())) { + MemberExpr *ME = new (Context) MemberExpr(PE, true, D, + IV->getLocation(), + D->getType(), + VK_LValue, OK_Ordinary); + Replacement = ME; + } else { + IV->setBase(PE); + } + } + } 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 (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + 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); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), + IV->getBase()->getLocEnd(), castExpr); + // 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); + } + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} + +#endif diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp new file mode 100644 index 0000000..722c5e8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp @@ -0,0 +1,39 @@ +//===--- 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/Rewrite/Frontend/Rewriters.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/TokenRewriter.h" +#include "llvm/Support/raw_ostream.h" + +void clang::DoRewriteTest(Preprocessor &PP, raw_ostream* OS) { + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + 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) + *OS << PP.getSpelling(*I); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp new file mode 100644 index 0000000..f701f72 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -0,0 +1,917 @@ +//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <vector> + +using namespace clang; +using namespace clang::serialized_diags; + +namespace { + +class AbbreviationMap { + llvm::DenseMap<unsigned, unsigned> Abbrevs; +public: + AbbreviationMap() {} + + void set(unsigned recordID, unsigned abbrevID) { + assert(Abbrevs.find(recordID) == Abbrevs.end() + && "Abbreviation already set."); + Abbrevs[recordID] = abbrevID; + } + + unsigned get(unsigned recordID) { + assert(Abbrevs.find(recordID) != Abbrevs.end() && + "Abbreviation not set."); + return Abbrevs[recordID]; + } +}; + +typedef SmallVector<uint64_t, 64> RecordData; +typedef SmallVectorImpl<uint64_t> RecordDataImpl; + +class SDiagsWriter; + +class SDiagsRenderer : public DiagnosticNoteRenderer { + SDiagsWriter &Writer; +public: + SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {} + + virtual ~SDiagsRenderer() {} + +protected: + void emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D) override; + + void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) override {} + + void emitNote(SourceLocation Loc, StringRef Message, + const SourceManager *SM) override; + + void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) override; + + void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) override; + void endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) override; +}; + +typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup; + +class SDiagsMerger : SerializedDiagnosticReader { + SDiagsWriter &Writer; + AbbrevLookup FileLookup; + AbbrevLookup CategoryLookup; + AbbrevLookup DiagFlagLookup; + +public: + SDiagsMerger(SDiagsWriter &Writer) + : SerializedDiagnosticReader(), Writer(Writer) {} + + std::error_code mergeRecordsFromFile(const char *File) { + return readDiagnostics(File); + } + +protected: + std::error_code visitStartOfDiagnostic() override; + std::error_code visitEndOfDiagnostic() override; + std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) override; + std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) override; + std::error_code visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) override; + std::error_code + visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) override; + +private: + std::error_code adjustSourceLocFilename(RecordData &Record, + unsigned int offset); + + void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup, + unsigned NewAbbrev); + + void writeRecordWithAbbrev(unsigned ID, RecordData &Record); + + void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob); +}; + +class SDiagsWriter : public DiagnosticConsumer { + friend class SDiagsRenderer; + friend class SDiagsMerger; + + struct SharedState; + + explicit SDiagsWriter(IntrusiveRefCntPtr<SharedState> State) + : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false), + State(State) {} + +public: + SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) + : LangOpts(nullptr), OriginalInstance(true), + MergeChildRecords(MergeChildRecords), + State(new SharedState(File, Diags)) { + if (MergeChildRecords) + RemoveOldDiagnostics(); + EmitPreamble(); + } + + ~SDiagsWriter() {} + + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override; + + void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override { + LangOpts = &LO; + } + + void finish() override; + +private: + /// \brief Build a DiagnosticsEngine to emit diagnostics about the diagnostics + DiagnosticsEngine *getMetaDiags(); + + /// \brief Remove old copies of the serialized diagnostics. This is necessary + /// so that we can detect when subprocesses write diagnostics that we should + /// merge into our own. + void RemoveOldDiagnostics(); + + /// \brief Emit the preamble for the serialized diagnostics. + void EmitPreamble(); + + /// \brief Emit the BLOCKINFO block. + void EmitBlockInfoBlock(); + + /// \brief Emit the META data block. + void EmitMetaBlock(); + + /// \brief Start a DIAG block. + void EnterDiagBlock(); + + /// \brief End a DIAG block. + void ExitDiagBlock(); + + /// \brief Emit a DIAG record. + void EmitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + const SourceManager *SM, + DiagOrStoredDiag D); + + /// \brief Emit FIXIT and SOURCE_RANGE records for a diagnostic. + void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM); + + /// \brief Emit a record for a CharSourceRange. + void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); + + /// \brief Emit the string information for the category. + unsigned getEmitCategory(unsigned category = 0); + + /// \brief Emit the string information for diagnostic flags. + unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID = 0); + + unsigned getEmitDiagnosticFlag(StringRef DiagName); + + /// \brief Emit (lazily) the file string and retrieved the file identifier. + unsigned getEmitFile(const char *Filename); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, const SourceManager *SM, + PresumedLoc PLoc, RecordDataImpl &Record, + unsigned TokSize = 0); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, + const SourceManager *SM, + unsigned TokSize = 0) { + AddLocToRecord(Loc, SM, SM ? SM->getPresumedLoc(Loc) : PresumedLoc(), + Record, TokSize); + } + + /// \brief Add CharSourceRange information the specified record. + void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, + const SourceManager &SM); + + /// \brief Language options, which can differ from one clone of this client + /// to another. + const LangOptions *LangOpts; + + /// \brief Whether this is the original instance (rather than one of its + /// clones), responsible for writing the file at the end. + bool OriginalInstance; + + /// \brief Whether this instance should aggregate diagnostics that are + /// generated from child processes. + bool MergeChildRecords; + + /// \brief State that is shared among the various clones of this diagnostic + /// consumer. + struct SharedState : RefCountedBase<SharedState> { + SharedState(StringRef File, DiagnosticOptions *Diags) + : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), + EmittedAnyDiagBlocks(false) {} + + /// \brief Diagnostic options. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + + /// \brief The byte buffer for the serialized content. + SmallString<1024> Buffer; + + /// \brief The BitStreamWriter for the serialized diagnostics. + llvm::BitstreamWriter Stream; + + /// \brief The name of the diagnostics file. + std::string OutputFile; + + /// \brief The set of constructed record abbreviations. + AbbreviationMap Abbrevs; + + /// \brief A utility buffer for constructing record content. + RecordData Record; + + /// \brief A text buffer for rendering diagnostic text. + SmallString<256> diagBuf; + + /// \brief The collection of diagnostic categories used. + llvm::DenseSet<unsigned> Categories; + + /// \brief The collection of files used. + llvm::DenseMap<const char *, unsigned> Files; + + typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> > + DiagFlagsTy; + + /// \brief Map for uniquing strings. + DiagFlagsTy DiagFlags; + + /// \brief Whether we have already started emission of any DIAG blocks. Once + /// this becomes \c true, we never close a DIAG block until we know that we're + /// starting another one or we're done. + bool EmittedAnyDiagBlocks; + + /// \brief Engine for emitting diagnostics about the diagnostics. + std::unique_ptr<DiagnosticsEngine> MetaDiagnostics; + }; + + /// \brief State shared among the various clones of this diagnostic consumer. + IntrusiveRefCntPtr<SharedState> State; +}; +} // end anonymous namespace + +namespace clang { +namespace serialized_diags { +std::unique_ptr<DiagnosticConsumer> +create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { + return llvm::make_unique<SDiagsWriter>(OutputFile, Diags, MergeChildRecords); +} + +} // end namespace serialized_diags +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Serialization methods. +//===----------------------------------------------------------------------===// + +/// \brief Emits a block ID in the BLOCKINFO block. +static void EmitBlockID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (!Name || Name[0] == 0) + return; + + Record.clear(); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +/// \brief Emits a record ID in the BLOCKINFO block. +static void EmitRecordID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record){ + Record.clear(); + Record.push_back(ID); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +void SDiagsWriter::AddLocToRecord(SourceLocation Loc, + const SourceManager *SM, + PresumedLoc PLoc, + RecordDataImpl &Record, + unsigned TokSize) { + if (PLoc.isInvalid()) { + // Emit a "sentinel" location. + Record.push_back((unsigned)0); // File. + Record.push_back((unsigned)0); // Line. + Record.push_back((unsigned)0); // Column. + Record.push_back((unsigned)0); // Offset. + return; + } + + Record.push_back(getEmitFile(PLoc.getFilename())); + Record.push_back(PLoc.getLine()); + Record.push_back(PLoc.getColumn()+TokSize); + Record.push_back(SM->getFileOffset(Loc)); +} + +void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, + RecordDataImpl &Record, + const SourceManager &SM) { + AddLocToRecord(Range.getBegin(), Record, &SM); + unsigned TokSize = 0; + if (Range.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(Range.getEnd(), + SM, *LangOpts); + + AddLocToRecord(Range.getEnd(), Record, &SM, TokSize); +} + +unsigned SDiagsWriter::getEmitFile(const char *FileName){ + if (!FileName) + return 0; + + unsigned &entry = State->Files[FileName]; + if (entry) + return entry; + + // Lazily generate the record for the file. + entry = State->Files.size(); + RecordData Record; + Record.push_back(RECORD_FILENAME); + Record.push_back(entry); + Record.push_back(0); // For legacy. + Record.push_back(0); // For legacy. + StringRef Name(FileName); + Record.push_back(Name.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record, + Name); + + return entry; +} + +void SDiagsWriter::EmitCharSourceRange(CharSourceRange R, + const SourceManager &SM) { + State->Record.clear(); + State->Record.push_back(RECORD_SOURCE_RANGE); + AddCharSourceRangeToRecord(R, State->Record, SM); + State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE), + State->Record); +} + +/// \brief Emits the preamble of the diagnostics file. +void SDiagsWriter::EmitPreamble() { + // Emit the file header. + State->Stream.Emit((unsigned)'D', 8); + State->Stream.Emit((unsigned)'I', 8); + State->Stream.Emit((unsigned)'A', 8); + State->Stream.Emit((unsigned)'G', 8); + + EmitBlockInfoBlock(); + EmitMetaBlock(); +} + +static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + using namespace llvm; + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; +} + +static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + AddSourceLocationAbbrev(Abbrev); + AddSourceLocationAbbrev(Abbrev); +} + +void SDiagsWriter::EmitBlockInfoBlock() { + State->Stream.EnterBlockInfoBlock(3); + + using namespace llvm; + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Meta" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_META, "Meta", Stream, Record); + EmitRecordID(RECORD_VERSION, "Version", Stream, Record); + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev)); + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Diagnostic" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); + EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); + EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); + EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); + EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); + EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); + EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); + + // Emit abbreviation for RECORD_DIAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. + AddSourceLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. + Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_CATEGORY. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. + Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_SOURCE_RANGE. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); + AddRangeLocationAbbrev(Abbrev); + Abbrevs.set(RECORD_SOURCE_RANGE, + Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit the abbreviation for RECORD_DIAG_FLAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. + Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FILENAME. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. + Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FIXIT. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); + AddRangeLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. + Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + Stream.ExitBlock(); +} + +void SDiagsWriter::EmitMetaBlock() { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + Stream.EnterSubblock(BLOCK_META, 3); + Record.clear(); + Record.push_back(RECORD_VERSION); + Record.push_back(VersionNumber); + Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); + Stream.ExitBlock(); +} + +unsigned SDiagsWriter::getEmitCategory(unsigned int category) { + if (!State->Categories.insert(category).second) + return category; + + // We use a local version of 'Record' so that we can be generating + // another record when we lazily generate one for the category entry. + RecordData Record; + Record.push_back(RECORD_CATEGORY); + Record.push_back(category); + StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); + Record.push_back(catName.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record, + catName); + + return category; +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID) { + if (DiagLevel == DiagnosticsEngine::Note) + return 0; // No flag for notes. + + StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); + return getEmitDiagnosticFlag(FlagName); +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) { + if (FlagName.empty()) + return 0; + + // Here we assume that FlagName points to static data whose pointer + // value is fixed. This allows us to unique by diagnostic groups. + const void *data = FlagName.data(); + std::pair<unsigned, StringRef> &entry = State->DiagFlags[data]; + if (entry.first == 0) { + entry.first = State->DiagFlags.size(); + entry.second = FlagName; + + // Lazily emit the string in a separate record. + RecordData Record; + Record.push_back(RECORD_DIAG_FLAG); + Record.push_back(entry.first); + Record.push_back(FlagName.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), + Record, FlagName); + } + + return entry.first; +} + +void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + // Enter the block for a non-note diagnostic immediately, rather than waiting + // for beginDiagnostic, in case associated notes are emitted before we get + // there. + if (DiagLevel != DiagnosticsEngine::Note) { + if (State->EmittedAnyDiagBlocks) + ExitDiagBlock(); + + EnterDiagBlock(); + State->EmittedAnyDiagBlocks = true; + } + + // Compute the diagnostic text. + State->diagBuf.clear(); + Info.FormatDiagnostic(State->diagBuf); + + if (Info.getLocation().isInvalid()) { + // Special-case diagnostics with no location. We may not have entered a + // source file in this case, so we can't use the normal DiagnosticsRenderer + // machinery. + + // Make sure we bracket all notes as "sub-diagnostics". This matches + // the behavior in SDiagsRenderer::emitDiagnostic(). + if (DiagLevel == DiagnosticsEngine::Note) + EnterDiagBlock(); + + EmitDiagnosticMessage(SourceLocation(), PresumedLoc(), DiagLevel, + State->diagBuf, nullptr, &Info); + + if (DiagLevel == DiagnosticsEngine::Note) + ExitDiagBlock(); + + return; + } + + assert(Info.hasSourceManager() && LangOpts && + "Unexpected diagnostic with valid location outside of a source file"); + SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts); + Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, + State->diagBuf.str(), + Info.getRanges(), + Info.getFixItHints(), + &Info.getSourceManager(), + &Info); +} + +static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) { + switch (Level) { +#define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X; + CASE(Ignored) + CASE(Note) + CASE(Remark) + CASE(Warning) + CASE(Error) + CASE(Fatal) +#undef CASE + } + + llvm_unreachable("invalid diagnostic level"); +} + +void SDiagsWriter::EmitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + const SourceManager *SM, + DiagOrStoredDiag D) { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // Emit the RECORD_DIAG record. + Record.clear(); + Record.push_back(RECORD_DIAG); + Record.push_back(getStableLevel(Level)); + AddLocToRecord(Loc, SM, PLoc, Record); + + if (const Diagnostic *Info = D.dyn_cast<const Diagnostic*>()) { + // Emit the category string lazily and get the category ID. + unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); + Record.push_back(getEmitCategory(DiagID)); + // Emit the diagnostic flag string lazily and get the mapped ID. + Record.push_back(getEmitDiagnosticFlag(Level, Info->getID())); + } else { + Record.push_back(getEmitCategory()); + Record.push_back(getEmitDiagnosticFlag(Level)); + } + + Record.push_back(Message.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message); +} + +void +SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D) { + Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, SM, D); +} + +void SDiagsWriter::EnterDiagBlock() { + State->Stream.EnterSubblock(BLOCK_DIAG, 4); +} + +void SDiagsWriter::ExitDiagBlock() { + State->Stream.ExitBlock(); +} + +void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + if (Level == DiagnosticsEngine::Note) + Writer.EnterDiagBlock(); +} + +void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + // Only end note diagnostics here, because we can't be sure when we've seen + // the last note associated with a non-note diagnostic. + if (Level == DiagnosticsEngine::Note) + Writer.ExitDiagBlock(); +} + +void SDiagsWriter::EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // Emit Source Ranges. + for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) + if (I->isValid()) + EmitCharSourceRange(*I, SM); + + // Emit FixIts. + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + const FixItHint &Fix = *I; + if (Fix.isNull()) + continue; + Record.clear(); + Record.push_back(RECORD_FIXIT); + AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM); + Record.push_back(Fix.CodeToInsert.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, + Fix.CodeToInsert); + } +} + +void SDiagsRenderer::emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + Writer.EmitCodeContext(Ranges, Hints, SM); +} + +void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message, + const SourceManager *SM) { + Writer.EnterDiagBlock(); + PresumedLoc PLoc = SM ? SM->getPresumedLoc(Loc) : PresumedLoc(); + Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, + Message, SM, DiagOrStoredDiag()); + Writer.ExitDiagBlock(); +} + +DiagnosticsEngine *SDiagsWriter::getMetaDiags() { + // FIXME: It's slightly absurd to create a new diagnostics engine here, but + // the other options that are available today are worse: + // + // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a + // part of. The DiagnosticsEngine would need to know not to send + // diagnostics back to the consumer that failed. This would require us to + // rework ChainedDiagnosticsConsumer and teach the engine about multiple + // consumers, which is difficult today because most APIs interface with + // consumers rather than the engine itself. + // + // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need + // to be distinct from the engine the writer was being added to and would + // normally not be used. + if (!State->MetaDiagnostics) { + IntrusiveRefCntPtr<DiagnosticIDs> IDs(new DiagnosticIDs()); + auto Client = + new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get()); + State->MetaDiagnostics = llvm::make_unique<DiagnosticsEngine>( + IDs, State->DiagOpts.get(), Client); + } + return State->MetaDiagnostics.get(); +} + +void SDiagsWriter::RemoveOldDiagnostics() { + if (!llvm::sys::fs::remove(State->OutputFile)) + return; + + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + // Disable merging child records, as whatever is in this file may be + // misleading. + MergeChildRecords = false; +} + +void SDiagsWriter::finish() { + // The original instance is responsible for writing the file. + if (!OriginalInstance) + return; + + // Finish off any diagnostic we were in the process of emitting. + if (State->EmittedAnyDiagBlocks) + ExitDiagBlock(); + + if (MergeChildRecords) { + if (!State->EmittedAnyDiagBlocks) + // We have no diagnostics of our own, so we can just leave the child + // process' output alone + return; + + if (llvm::sys::fs::exists(State->OutputFile)) + if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str())) + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + } + + std::error_code EC; + auto OS = llvm::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(), + EC, llvm::sys::fs::F_None); + if (EC) { + getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) + << State->OutputFile << EC.message(); + return; + } + + // Write the generated bitstream to "Out". + OS->write((char *)&State->Buffer.front(), State->Buffer.size()); + OS->flush(); +} + +std::error_code SDiagsMerger::visitStartOfDiagnostic() { + Writer.EnterDiagBlock(); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitEndOfDiagnostic() { + Writer.ExitDiagBlock(); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) { + RecordData Record; + Record.push_back(RECORD_SOURCE_RANGE); + Record.push_back(FileLookup[Start.FileID]); + Record.push_back(Start.Line); + Record.push_back(Start.Col); + Record.push_back(Start.Offset); + Record.push_back(FileLookup[End.FileID]); + Record.push_back(End.Line); + Record.push_back(End.Col); + Record.push_back(End.Offset); + + Writer.State->Stream.EmitRecordWithAbbrev( + Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + RecordData MergedRecord; + MergedRecord.push_back(RECORD_DIAG); + MergedRecord.push_back(Severity); + MergedRecord.push_back(FileLookup[Location.FileID]); + MergedRecord.push_back(Location.Line); + MergedRecord.push_back(Location.Col); + MergedRecord.push_back(Location.Offset); + MergedRecord.push_back(CategoryLookup[Category]); + MergedRecord.push_back(Flag ? DiagFlagLookup[Flag] : 0); + MergedRecord.push_back(Message.size()); + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_DIAG), MergedRecord, Message); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef Text) { + RecordData Record; + Record.push_back(RECORD_FIXIT); + Record.push_back(FileLookup[Start.FileID]); + Record.push_back(Start.Line); + Record.push_back(Start.Col); + Record.push_back(Start.Offset); + Record.push_back(FileLookup[End.FileID]); + Record.push_back(End.Line); + Record.push_back(End.Col); + Record.push_back(End.Offset); + Record.push_back(Text.size()); + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + FileLookup[ID] = Writer.getEmitFile(Name.str().c_str()); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { + CategoryLookup[ID] = Writer.getEmitCategory(ID); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) { + DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name); + return std::error_code(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp new file mode 100644 index 0000000..0ebbd22 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp @@ -0,0 +1,295 @@ +//===--- SerializedDiagnosticReader.cpp - Reads diagnostics ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; +using namespace clang::serialized_diags; + +std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { + // Open the diagnostics file. + FileSystemOptions FO; + FileManager FileMgr(FO); + + auto Buffer = FileMgr.getBufferForFile(File); + if (!Buffer) + return SDError::CouldNotLoad; + + llvm::BitstreamReader StreamFile; + StreamFile.init((const unsigned char *)(*Buffer)->getBufferStart(), + (const unsigned char *)(*Buffer)->getBufferEnd()); + + llvm::BitstreamCursor Stream(StreamFile); + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || + Stream.Read(8) != 'I' || + Stream.Read(8) != 'A' || + Stream.Read(8) != 'G') + return SDError::InvalidSignature; + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + if (Stream.ReadCode() != llvm::bitc::ENTER_SUBBLOCK) + return SDError::InvalidDiagnostics; + + std::error_code EC; + switch (Stream.ReadSubBlockID()) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (Stream.ReadBlockInfoBlock()) + return SDError::MalformedBlockInfoBlock; + continue; + case BLOCK_META: + if ((EC = readMetaBlock(Stream))) + return EC; + continue; + case BLOCK_DIAG: + if ((EC = readDiagnosticBlock(Stream))) + return EC; + continue; + default: + if (!Stream.SkipBlock()) + return SDError::MalformedTopLevelBlock; + continue; + } + } + return std::error_code(); +} + +enum class SerializedDiagnosticReader::Cursor { + Record = 1, + BlockEnd, + BlockBegin +}; + +llvm::ErrorOr<SerializedDiagnosticReader::Cursor> +SerializedDiagnosticReader::skipUntilRecordOrBlock( + llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { + BlockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + BlockOrRecordID = Stream.ReadSubBlockID(); + return Cursor::BlockBegin; + + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) + return SDError::InvalidDiagnostics; + return Cursor::BlockEnd; + + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + + case llvm::bitc::UNABBREV_RECORD: + return SDError::UnsupportedConstruct; + + default: + // We found a record. + BlockOrRecordID = Code; + return Cursor::Record; + } + } + + return SDError::InvalidDiagnostics; +} + +std::error_code +SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) + return SDError::MalformedMetadataBlock; + + bool VersionChecked = false; + + while (true) { + unsigned BlockOrCode = 0; + llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + if (!Res) + Res.getError(); + + switch (Res.get()) { + case Cursor::Record: + break; + case Cursor::BlockBegin: + if (Stream.SkipBlock()) + return SDError::MalformedMetadataBlock; + case Cursor::BlockEnd: + if (!VersionChecked) + return SDError::MissingVersion; + return std::error_code(); + } + + SmallVector<uint64_t, 1> Record; + unsigned RecordID = Stream.readRecord(BlockOrCode, Record); + + if (RecordID == RECORD_VERSION) { + if (Record.size() < 1) + return SDError::MissingVersion; + if (Record[0] > VersionNumber) + return SDError::VersionMismatch; + VersionChecked = true; + } + } +} + +std::error_code +SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) + return SDError::MalformedDiagnosticBlock; + + std::error_code EC; + if ((EC = visitStartOfDiagnostic())) + return EC; + + SmallVector<uint64_t, 16> Record; + while (true) { + unsigned BlockOrCode = 0; + llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + if (!Res) + Res.getError(); + + switch (Res.get()) { + case Cursor::BlockBegin: + // The only blocks we care about are subdiagnostics. + if (BlockOrCode == serialized_diags::BLOCK_DIAG) { + if ((EC = readDiagnosticBlock(Stream))) + return EC; + } else if (!Stream.SkipBlock()) + return SDError::MalformedSubBlock; + continue; + case Cursor::BlockEnd: + if ((EC = visitEndOfDiagnostic())) + return EC; + return std::error_code(); + case Cursor::Record: + break; + } + + // Read the record. + Record.clear(); + StringRef Blob; + unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob); + + if (RecID < serialized_diags::RECORD_FIRST || + RecID > serialized_diags::RECORD_LAST) + continue; + + switch ((RecordIDs)RecID) { + case RECORD_CATEGORY: + // A category has ID and name size. + if (Record.size() != 2) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitCategoryRecord(Record[0], Blob))) + return EC; + continue; + case RECORD_DIAG: + // A diagnostic has severity, location (4), category, flag, and message + // size. + if (Record.size() != 8) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitDiagnosticRecord( + Record[0], Location(Record[1], Record[2], Record[3], Record[4]), + Record[5], Record[6], Blob))) + return EC; + continue; + case RECORD_DIAG_FLAG: + // A diagnostic flag has ID and name size. + if (Record.size() != 2) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitDiagFlagRecord(Record[0], Blob))) + return EC; + continue; + case RECORD_FILENAME: + // A filename has ID, size, timestamp, and name size. The size and + // timestamp are legacy fields that are always zero these days. + if (Record.size() != 4) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob))) + return EC; + continue; + case RECORD_FIXIT: + // A fixit has two locations (4 each) and message size. + if (Record.size() != 9) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitFixitRecord( + Location(Record[0], Record[1], Record[2], Record[3]), + Location(Record[4], Record[5], Record[6], Record[7]), Blob))) + return EC; + continue; + case RECORD_SOURCE_RANGE: + // A source range is two locations (4 each). + if (Record.size() != 8) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitSourceRangeRecord( + Location(Record[0], Record[1], Record[2], Record[3]), + Location(Record[4], Record[5], Record[6], Record[7])))) + return EC; + continue; + case RECORD_VERSION: + // A version is just a number. + if (Record.size() != 1) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitVersionRecord(Record[0]))) + return EC; + continue; + } + } +} + +namespace { +class SDErrorCategoryType final : public std::error_category { + const char *name() const LLVM_NOEXCEPT override { + return "clang.serialized_diags"; + } + std::string message(int IE) const override { + SDError E = static_cast<SDError>(IE); + switch (E) { + case SDError::CouldNotLoad: + return "Failed to open diagnostics file"; + case SDError::InvalidSignature: + return "Invalid diagnostics signature"; + case SDError::InvalidDiagnostics: + return "Parse error reading diagnostics"; + case SDError::MalformedTopLevelBlock: + return "Malformed block at top-level of diagnostics"; + case SDError::MalformedSubBlock: + return "Malformed sub-block in a diagnostic"; + case SDError::MalformedBlockInfoBlock: + return "Malformed BlockInfo block"; + case SDError::MalformedMetadataBlock: + return "Malformed Metadata block"; + case SDError::MalformedDiagnosticBlock: + return "Malformed Diagnostic block"; + case SDError::MalformedDiagnosticRecord: + return "Malformed Diagnostic record"; + case SDError::MissingVersion: + return "No version provided in diagnostics"; + case SDError::VersionMismatch: + return "Unsupported diagnostics version"; + case SDError::UnsupportedConstruct: + return "Bitcode constructs that are not supported in diagnostics appear"; + case SDError::HandlerFailed: + return "Generic error occurred while handling a record"; + } + llvm_unreachable("Unknown error type!"); + } +}; +} + +static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; +const std::error_category &clang::serialized_diags::SDErrorCategory() { + return *ErrorCategory; +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp new file mode 100644 index 0000000..bbc9914 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp @@ -0,0 +1,1261 @@ +//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnostic.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Locale.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace clang; + +static const enum raw_ostream::Colors noteColor = + raw_ostream::BLACK; +static const enum raw_ostream::Colors remarkColor = + raw_ostream::BLUE; +static const enum raw_ostream::Colors fixitColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors caretColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors warningColor = + raw_ostream::MAGENTA; +static const enum raw_ostream::Colors templateColor = + raw_ostream::CYAN; +static const enum raw_ostream::Colors errorColor = raw_ostream::RED; +static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; +// Used for changing only the bold attribute. +static const enum raw_ostream::Colors savedColor = + raw_ostream::SAVEDCOLOR; + +/// \brief Add highlights to differences in template strings. +static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, + bool &Normal, bool Bold) { + while (1) { + size_t Pos = Str.find(ToggleHighlight); + OS << Str.slice(0, Pos); + if (Pos == StringRef::npos) + break; + + Str = Str.substr(Pos + 1); + if (Normal) + OS.changeColor(templateColor, true); + else { + OS.resetColor(); + if (Bold) + OS.changeColor(savedColor, true); + } + Normal = !Normal; + } +} + +/// \brief Number of spaces to indent when word-wrapping. +const unsigned WordWrapIndentation = 6; + +static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { + int bytes = 0; + while (0<i) { + if (SourceLine[--i]=='\t') + break; + ++bytes; + } + return bytes; +} + +/// \brief returns a printable representation of first item from input range +/// +/// This function returns a printable representation of the next item in a line +/// of source. If the next byte begins a valid and printable character, that +/// character is returned along with 'true'. +/// +/// Otherwise, if the next byte begins a valid, but unprintable character, a +/// printable, escaped representation of the character is returned, along with +/// 'false'. Otherwise a printable, escaped representation of the next byte +/// is returned along with 'false'. +/// +/// \note The index is updated to be used with a subsequent call to +/// printableTextForNextCharacter. +/// +/// \param SourceLine The line of source +/// \param i Pointer to byte index, +/// \param TabStop used to expand tabs +/// \return pair(printable text, 'true' iff original text was printable) +/// +static std::pair<SmallString<16>, bool> +printableTextForNextCharacter(StringRef SourceLine, size_t *i, + unsigned TabStop) { + assert(i && "i must not be null"); + assert(*i<SourceLine.size() && "must point to a valid index"); + + if (SourceLine[*i]=='\t') { + assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && + "Invalid -ftabstop value"); + unsigned col = bytesSincePreviousTabOrLineBegin(SourceLine, *i); + unsigned NumSpaces = TabStop - col%TabStop; + assert(0 < NumSpaces && NumSpaces <= TabStop + && "Invalid computation of space amt"); + ++(*i); + + SmallString<16> expandedTab; + expandedTab.assign(NumSpaces, ' '); + return std::make_pair(expandedTab, true); + } + + unsigned char const *begin, *end; + begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i)); + end = begin + (SourceLine.size() - *i); + + if (isLegalUTF8Sequence(begin, end)) { + UTF32 c; + UTF32 *cptr = &c; + unsigned char const *original_begin = begin; + unsigned char const *cp_end = begin+getNumBytesForUTF8(SourceLine[*i]); + + ConversionResult res = ConvertUTF8toUTF32(&begin, cp_end, &cptr, cptr+1, + strictConversion); + (void)res; + assert(conversionOK==res); + assert(0 < begin-original_begin + && "we must be further along in the string now"); + *i += begin-original_begin; + + if (!llvm::sys::locale::isPrint(c)) { + // If next character is valid UTF-8, but not printable + SmallString<16> expandedCP("<U+>"); + while (c) { + expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16)); + c/=16; + } + while (expandedCP.size() < 8) + expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0)); + return std::make_pair(expandedCP, false); + } + + // If next character is valid UTF-8, and printable + return std::make_pair(SmallString<16>(original_begin, cp_end), true); + + } + + // If next byte is not valid UTF-8 (and therefore not printable) + SmallString<16> expandedByte("<XX>"); + unsigned char byte = SourceLine[*i]; + expandedByte[1] = llvm::hexdigit(byte / 16); + expandedByte[2] = llvm::hexdigit(byte % 16); + ++(*i); + return std::make_pair(expandedByte, false); +} + +static void expandTabs(std::string &SourceLine, unsigned TabStop) { + size_t i = SourceLine.size(); + while (i>0) { + i--; + if (SourceLine[i]!='\t') + continue; + size_t tmp_i = i; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop); + SourceLine.replace(i, 1, res.first.c_str()); + } +} + +/// This function takes a raw source line and produces a mapping from the bytes +/// of the printable representation of the line to the columns those printable +/// characters will appear at (numbering the first column as 0). +/// +/// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab +/// character) then the array will map that byte to the first column the +/// tab appears at and the next value in the map will have been incremented +/// more than once. +/// +/// If a byte is the first in a sequence of bytes that together map to a single +/// entity in the output, then the array will map that byte to the appropriate +/// column while the subsequent bytes will be -1. +/// +/// The last element in the array does not correspond to any byte in the input +/// and instead is the number of columns needed to display the source +/// +/// example: (given a tabstop of 8) +/// +/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} +/// +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// display) +static void byteToColumn(StringRef SourceLine, unsigned TabStop, + SmallVectorImpl<int> &out) { + out.clear(); + + if (SourceLine.empty()) { + out.resize(1u,0); + return; + } + + out.resize(SourceLine.size()+1, -1); + + int columns = 0; + size_t i = 0; + while (i<SourceLine.size()) { + out[i] = columns; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &i, TabStop); + columns += llvm::sys::locale::columnWidth(res.first); + } + out.back() = columns; +} + +/// This function takes a raw source line and produces a mapping from columns +/// to the byte of the source line that produced the character displaying at +/// that column. This is the inverse of the mapping produced by byteToColumn() +/// +/// The last element in the array is the number of bytes in the source string +/// +/// example: (given a tabstop of 8) +/// +/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} +/// +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// display) +static void columnToByte(StringRef SourceLine, unsigned TabStop, + SmallVectorImpl<int> &out) { + out.clear(); + + if (SourceLine.empty()) { + out.resize(1u, 0); + return; + } + + int columns = 0; + size_t i = 0; + while (i<SourceLine.size()) { + out.resize(columns+1, -1); + out.back() = i; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &i, TabStop); + columns += llvm::sys::locale::columnWidth(res.first); + } + out.resize(columns+1, -1); + out.back() = i; +} + +namespace { +struct SourceColumnMap { + SourceColumnMap(StringRef SourceLine, unsigned TabStop) + : m_SourceLine(SourceLine) { + + ::byteToColumn(SourceLine, TabStop, m_byteToColumn); + ::columnToByte(SourceLine, TabStop, m_columnToByte); + + assert(m_byteToColumn.size()==SourceLine.size()+1); + assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); + assert(m_byteToColumn.size() + == static_cast<unsigned>(m_columnToByte.back()+1)); + assert(static_cast<unsigned>(m_byteToColumn.back()+1) + == m_columnToByte.size()); + } + int columns() const { return m_byteToColumn.back(); } + int bytes() const { return m_columnToByte.back(); } + + /// \brief Map a byte to the column which it is at the start of, or return -1 + /// if it is not at the start of a column (for a UTF-8 trailing byte). + int byteToColumn(int n) const { + assert(0<=n && n<static_cast<int>(m_byteToColumn.size())); + return m_byteToColumn[n]; + } + + /// \brief Map a byte to the first column which contains it. + int byteToContainingColumn(int N) const { + assert(0 <= N && N < static_cast<int>(m_byteToColumn.size())); + while (m_byteToColumn[N] == -1) + --N; + return m_byteToColumn[N]; + } + + /// \brief Map a column to the byte which starts the column, or return -1 if + /// the column the second or subsequent column of an expanded tab or similar + /// multi-column entity. + int columnToByte(int n) const { + assert(0<=n && n<static_cast<int>(m_columnToByte.size())); + return m_columnToByte[n]; + } + + /// \brief Map from a byte index to the next byte which starts a column. + int startOfNextColumn(int N) const { + assert(0 <= N && N < static_cast<int>(m_byteToColumn.size() - 1)); + while (byteToColumn(++N) == -1) {} + return N; + } + + /// \brief Map from a byte index to the previous byte which starts a column. + int startOfPreviousColumn(int N) const { + assert(0 < N && N < static_cast<int>(m_byteToColumn.size())); + while (byteToColumn(--N) == -1) {} + return N; + } + + StringRef getSourceLine() const { + return m_SourceLine; + } + +private: + const std::string m_SourceLine; + SmallVector<int,200> m_byteToColumn; + SmallVector<int,200> m_columnToByte; +}; +} // end anonymous namespace + +/// \brief When the source code line we want to print is too long for +/// the terminal, select the "interesting" region. +static void selectInterestingSourceRegion(std::string &SourceLine, + std::string &CaretLine, + std::string &FixItInsertionLine, + unsigned Columns, + const SourceColumnMap &map) { + unsigned CaretColumns = CaretLine.size(); + unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine); + unsigned MaxColumns = std::max(static_cast<unsigned>(map.columns()), + std::max(CaretColumns, FixItColumns)); + // if the number of columns is less than the desired number we're done + if (MaxColumns <= Columns) + return; + + // No special characters are allowed in CaretLine. + assert(CaretLine.end() == + std::find_if(CaretLine.begin(), CaretLine.end(), + [](char c) { return c < ' ' || '~' < c; })); + + // Find the slice that we need to display the full caret line + // correctly. + unsigned CaretStart = 0, CaretEnd = CaretLine.size(); + for (; CaretStart != CaretEnd; ++CaretStart) + if (!isWhitespace(CaretLine[CaretStart])) + break; + + for (; CaretEnd != CaretStart; --CaretEnd) + if (!isWhitespace(CaretLine[CaretEnd - 1])) + break; + + // caret has already been inserted into CaretLine so the above whitespace + // check is guaranteed to include the caret + + // If we have a fix-it line, make sure the slice includes all of the + // fix-it information. + if (!FixItInsertionLine.empty()) { + unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); + for (; FixItStart != FixItEnd; ++FixItStart) + if (!isWhitespace(FixItInsertionLine[FixItStart])) + break; + + for (; FixItEnd != FixItStart; --FixItEnd) + if (!isWhitespace(FixItInsertionLine[FixItEnd - 1])) + break; + + // We can safely use the byte offset FixItStart as the column offset + // because the characters up until FixItStart are all ASCII whitespace + // characters. + unsigned FixItStartCol = FixItStart; + unsigned FixItEndCol + = llvm::sys::locale::columnWidth(FixItInsertionLine.substr(0, FixItEnd)); + + CaretStart = std::min(FixItStartCol, CaretStart); + CaretEnd = std::max(FixItEndCol, CaretEnd); + } + + // CaretEnd may have been set at the middle of a character + // If it's not at a character's first column then advance it past the current + // character. + while (static_cast<int>(CaretEnd) < map.columns() && + -1 == map.columnToByte(CaretEnd)) + ++CaretEnd; + + assert((static_cast<int>(CaretStart) > map.columns() || + -1!=map.columnToByte(CaretStart)) && + "CaretStart must not point to a column in the middle of a source" + " line character"); + assert((static_cast<int>(CaretEnd) > map.columns() || + -1!=map.columnToByte(CaretEnd)) && + "CaretEnd must not point to a column in the middle of a source line" + " character"); + + // CaretLine[CaretStart, CaretEnd) contains all of the interesting + // parts of the caret line. While this slice is smaller than the + // number of columns we have, try to grow the slice to encompass + // more context. + + unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart, + map.columns())); + unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd, + map.columns())); + + unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart + - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart)); + + char const *front_ellipse = " ..."; + char const *front_space = " "; + char const *back_ellipse = "..."; + unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse); + + unsigned TargetColumns = Columns; + // Give us extra room for the ellipses + // and any of the caret line that extends past the source + if (TargetColumns > ellipses_space+CaretColumnsOutsideSource) + TargetColumns -= ellipses_space+CaretColumnsOutsideSource; + + while (SourceStart>0 || SourceEnd<SourceLine.size()) { + bool ExpandedRegion = false; + + if (SourceStart>0) { + unsigned NewStart = map.startOfPreviousColumn(SourceStart); + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + // FIXME: Detect non-ASCII whitespace characters too. + while (NewStart && isWhitespace(SourceLine[NewStart])) + NewStart = map.startOfPreviousColumn(NewStart); + + // Skip over this bit of "interesting" text. + while (NewStart) { + unsigned Prev = map.startOfPreviousColumn(NewStart); + if (isWhitespace(SourceLine[Prev])) + break; + NewStart = Prev; + } + + assert(map.byteToColumn(NewStart) != -1); + unsigned NewColumns = map.byteToColumn(SourceEnd) - + map.byteToColumn(NewStart); + if (NewColumns <= TargetColumns) { + SourceStart = NewStart; + ExpandedRegion = true; + } + } + + if (SourceEnd<SourceLine.size()) { + unsigned NewEnd = map.startOfNextColumn(SourceEnd); + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + // FIXME: Detect non-ASCII whitespace characters too. + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) + NewEnd = map.startOfNextColumn(NewEnd); + + // Skip over this bit of "interesting" text. + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) + NewEnd = map.startOfNextColumn(NewEnd); + + assert(map.byteToColumn(NewEnd) != -1); + unsigned NewColumns = map.byteToColumn(NewEnd) - + map.byteToColumn(SourceStart); + if (NewColumns <= TargetColumns) { + SourceEnd = NewEnd; + ExpandedRegion = true; + } + } + + if (!ExpandedRegion) + break; + } + + CaretStart = map.byteToColumn(SourceStart); + CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; + + // [CaretStart, CaretEnd) is the slice we want. Update the various + // output lines to show only this slice, with two-space padding + // before the lines so that it looks nicer. + + assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && + SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); + assert(SourceStart <= SourceEnd); + assert(CaretStart <= CaretEnd); + + unsigned BackColumnsRemoved + = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd); + unsigned FrontColumnsRemoved = CaretStart; + unsigned ColumnsKept = CaretEnd-CaretStart; + + // We checked up front that the line needed truncation + assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns); + + // The line needs some truncation, and we'd prefer to keep the front + // if possible, so remove the back + if (BackColumnsRemoved > strlen(back_ellipse)) + SourceLine.replace(SourceEnd, std::string::npos, back_ellipse); + + // If that's enough then we're done + if (FrontColumnsRemoved+ColumnsKept <= Columns) + return; + + // Otherwise remove the front as well + if (FrontColumnsRemoved > strlen(front_ellipse)) { + SourceLine.replace(0, SourceStart, front_ellipse); + CaretLine.replace(0, CaretStart, front_space); + if (!FixItInsertionLine.empty()) + FixItInsertionLine.replace(0, CaretStart, front_space); + } +} + +/// \brief Skip over whitespace in the string, starting at the given +/// index. +/// +/// \returns The index of the first non-whitespace character that is +/// greater than or equal to Idx or, if no such character exists, +/// returns the end of the string. +static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { + while (Idx < Length && isWhitespace(Str[Idx])) + ++Idx; + return Idx; +} + +/// \brief If the given character is the start of some kind of +/// balanced punctuation (e.g., quotes or parentheses), return the +/// character that will terminate the punctuation. +/// +/// \returns The ending punctuation character, if any, or the NULL +/// character if the input character does not start any punctuation. +static inline char findMatchingPunctuation(char c) { + switch (c) { + case '\'': return '\''; + case '`': return '\''; + case '"': return '"'; + case '(': return ')'; + case '[': return ']'; + case '{': return '}'; + default: break; + } + + return 0; +} + +/// \brief Find the end of the word starting at the given offset +/// within a string. +/// +/// \returns the index pointing one character past the end of the +/// word. +static unsigned findEndOfWord(unsigned Start, StringRef Str, + unsigned Length, unsigned Column, + unsigned Columns) { + assert(Start < Str.size() && "Invalid start position!"); + unsigned End = Start + 1; + + // If we are already at the end of the string, take that as the word. + if (End == Str.size()) + return End; + + // Determine if the start of the string is actually opening + // punctuation, e.g., a quote or parentheses. + char EndPunct = findMatchingPunctuation(Str[Start]); + if (!EndPunct) { + // This is a normal word. Just find the first space character. + while (End < Length && !isWhitespace(Str[End])) + ++End; + return End; + } + + // We have the start of a balanced punctuation sequence (quotes, + // parentheses, etc.). Determine the full sequence is. + SmallString<16> PunctuationEndStack; + PunctuationEndStack.push_back(EndPunct); + while (End < Length && !PunctuationEndStack.empty()) { + if (Str[End] == PunctuationEndStack.back()) + PunctuationEndStack.pop_back(); + else if (char SubEndPunct = findMatchingPunctuation(Str[End])) + PunctuationEndStack.push_back(SubEndPunct); + + ++End; + } + + // Find the first space character after the punctuation ended. + while (End < Length && !isWhitespace(Str[End])) + ++End; + + unsigned PunctWordLength = End - Start; + if (// If the word fits on this line + Column + PunctWordLength <= Columns || + // ... or the word is "short enough" to take up the next line + // without too much ugly white space + PunctWordLength < Columns/3) + return End; // Take the whole thing as a single "word". + + // The whole quoted/parenthesized string is too long to print as a + // single "word". Instead, find the "word" that starts just after + // the punctuation and use that end-point instead. This will recurse + // until it finds something small enough to consider a word. + return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); +} + +/// \brief Print the given string to a stream, word-wrapping it to +/// some number of columns in the process. +/// +/// \param OS the stream to which the word-wrapping string will be +/// emitted. +/// \param Str the string to word-wrap and output. +/// \param Columns the number of columns to word-wrap to. +/// \param Column the column number at which the first character of \p +/// Str will be printed. This will be non-zero when part of the first +/// line has already been printed. +/// \param Bold if the current text should be bold +/// \param Indentation the number of spaces to indent any lines beyond +/// the first line. +/// \returns true if word-wrapping was required, or false if the +/// string fit on the first line. +static bool printWordWrapped(raw_ostream &OS, StringRef Str, + unsigned Columns, + unsigned Column = 0, + bool Bold = false, + unsigned Indentation = WordWrapIndentation) { + const unsigned Length = std::min(Str.find('\n'), Str.size()); + bool TextNormal = true; + + // The string used to indent each line. + SmallString<16> IndentStr; + IndentStr.assign(Indentation, ' '); + bool Wrapped = false; + for (unsigned WordStart = 0, WordEnd; WordStart < Length; + WordStart = WordEnd) { + // Find the beginning of the next word. + WordStart = skipWhitespace(WordStart, Str, Length); + if (WordStart == Length) + break; + + // Find the end of this word. + WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); + + // Does this word fit on the current line? + unsigned WordLength = WordEnd - WordStart; + if (Column + WordLength < Columns) { + // This word fits on the current line; print it there. + if (WordStart) { + OS << ' '; + Column += 1; + } + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); + Column += WordLength; + continue; + } + + // This word does not fit on the current line, so wrap to the next + // line. + OS << '\n'; + OS.write(&IndentStr[0], Indentation); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); + Column = Indentation + WordLength; + Wrapped = true; + } + + // Append any remaning text from the message with its existing formatting. + applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); + + assert(TextNormal && "Text highlighted at end of diagnostic message."); + + return Wrapped; +} + +TextDiagnostic::TextDiagnostic(raw_ostream &OS, + const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {} + +TextDiagnostic::~TextDiagnostic() {} + +void +TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D) { + uint64_t StartOfLocationInfo = OS.tell(); + + // Emit the location of this particular diagnostic. + if (Loc.isValid()) + emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM); + + if (DiagOpts->ShowColors) + OS.resetColor(); + + printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); + printDiagnosticMessage(OS, + /*IsSupplemental*/ Level == DiagnosticsEngine::Note, + Message, OS.tell() - StartOfLocationInfo, + DiagOpts->MessageLength, DiagOpts->ShowColors); +} + +/*static*/ void +TextDiagnostic::printDiagnosticLevel(raw_ostream &OS, + DiagnosticsEngine::Level Level, + bool ShowColors, + bool CLFallbackMode) { + if (ShowColors) { + // Print diagnostic category in bold and color + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; + case DiagnosticsEngine::Remark: OS.changeColor(remarkColor, true); break; + case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; + case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; + case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; + } + } + + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS << "note"; break; + case DiagnosticsEngine::Remark: OS << "remark"; break; + case DiagnosticsEngine::Warning: OS << "warning"; break; + case DiagnosticsEngine::Error: OS << "error"; break; + case DiagnosticsEngine::Fatal: OS << "fatal error"; break; + } + + // In clang-cl /fallback mode, print diagnostics as "error(clang):". This + // makes it more clear whether a message is coming from clang or cl.exe, + // and it prevents MSBuild from concluding that the build failed just because + // there is an "error:" in the output. + if (CLFallbackMode) + OS << "(clang)"; + + OS << ": "; + + if (ShowColors) + OS.resetColor(); +} + +/*static*/ +void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, + bool IsSupplemental, + StringRef Message, + unsigned CurrentColumn, + unsigned Columns, bool ShowColors) { + bool Bold = false; + if (ShowColors && !IsSupplemental) { + // Print primary diagnostic messages in bold and without color, to visually + // indicate the transition from continuation notes and other output. + OS.changeColor(savedColor, true); + Bold = true; + } + + if (Columns) + printWordWrapped(OS, Message, Columns, CurrentColumn, Bold); + else { + bool Normal = true; + applyTemplateHighlighting(OS, Message, Normal, Bold); + assert(Normal && "Formatting should have returned to normal"); + } + + if (ShowColors) + OS.resetColor(); + OS << '\n'; +} + +/// \brief Print out the file/line/column information and include trace. +/// +/// This method handlen the emission of the diagnostic location information. +/// This includes extracting as much location information as is present for +/// the diagnostic and printing it, as well as any include stack or source +/// ranges necessary. +void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) { + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Loc); + if (!FID.isInvalid()) { + const FileEntry* FE = SM.getFileEntryForID(FID); + if (FE && FE->isValid()) { + OS << FE->getName(); + if (FE->isInPCH()) + OS << " (in PCH)"; + OS << ": "; + } + } + return; + } + unsigned LineNo = PLoc.getLine(); + + if (!DiagOpts->ShowLocation) + return; + + if (DiagOpts->ShowColors) + OS.changeColor(savedColor, true); + + OS << PLoc.getFilename(); + switch (DiagOpts->getFormat()) { + case DiagnosticOptions::Clang: OS << ':' << LineNo; break; + case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; + case DiagnosticOptions::Vi: OS << " +" << LineNo; break; + } + + if (DiagOpts->ShowColumn) + // Compute the column number. + if (unsigned ColNo = PLoc.getColumn()) { + if (DiagOpts->getFormat() == DiagnosticOptions::Msvc) { + OS << ','; + // Visual Studio 2010 or earlier expects column number to be off by one + if (LangOpts.MSCompatibilityVersion && + LangOpts.MSCompatibilityVersion < 170000000) + ColNo--; + } else + OS << ':'; + OS << ColNo; + } + switch (DiagOpts->getFormat()) { + case DiagnosticOptions::Clang: + case DiagnosticOptions::Vi: OS << ':'; break; + case DiagnosticOptions::Msvc: OS << ") : "; break; + } + + if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { + FileID CaretFileID = + SM.getFileID(SM.getExpansionLoc(Loc)); + bool PrintedRange = false; + + for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), + RE = Ranges.end(); + RI != RE; ++RI) { + // Ignore invalid ranges. + if (!RI->isValid()) continue; + + SourceLocation B = SM.getExpansionLoc(RI->getBegin()); + SourceLocation E = SM.getExpansionLoc(RI->getEnd()); + + // If the End location and the start location are the same and are a + // macro location, then the range was something that came from a + // macro expansion or _Pragma. If this is an object-like macro, the + // best we can do is to highlight the range. If this is a + // function-like macro, we'd also like to highlight the arguments. + if (B == E && RI->getEnd().isMacroID()) + E = SM.getExpansionRange(RI->getEnd()).second; + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); + + // If the start or end of the range is in another file, just discard + // it. + if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) + continue; + + // Add in the length of the token, so that we cover multi-char + // tokens. + unsigned TokSize = 0; + if (RI->isTokenRange()) + TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); + + OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' + << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' + << SM.getLineNumber(EInfo.first, EInfo.second) << ':' + << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) + << '}'; + PrintedRange = true; + } + + if (PrintedRange) + OS << ':'; + } + OS << ' '; +} + +void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc, + const SourceManager &SM) { + if (DiagOpts->ShowLocation) + OS << "In file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; +} + +void TextDiagnostic::emitImportLocation(SourceLocation Loc, PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation) + OS << "In module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "In module " << ModuleName << "':\n"; +} + +void TextDiagnostic::emitBuildingModuleLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation && PLoc.getFilename()) + OS << "While building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "While building module '" << ModuleName << "':\n"; +} + +/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. +static void highlightRange(const CharSourceRange &R, + unsigned LineNo, FileID FID, + const SourceColumnMap &map, + std::string &CaretLine, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (!R.isValid()) return; + + SourceLocation Begin = R.getBegin(); + SourceLocation End = R.getEnd(); + + unsigned StartLineNo = SM.getExpansionLineNumber(Begin); + if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) + return; // No intersection. + + unsigned EndLineNo = SM.getExpansionLineNumber(End); + if (EndLineNo < LineNo || SM.getFileID(End) != FID) + return; // No intersection. + + // Compute the column number of the start. + unsigned StartColNo = 0; + if (StartLineNo == LineNo) { + StartColNo = SM.getExpansionColumnNumber(Begin); + if (StartColNo) --StartColNo; // Zero base the col #. + } + + // Compute the column number of the end. + unsigned EndColNo = map.getSourceLine().size(); + if (EndLineNo == LineNo) { + EndColNo = SM.getExpansionColumnNumber(End); + if (EndColNo) { + --EndColNo; // Zero base the col #. + + // Add in the length of the token, so that we cover multi-char tokens if + // this is a token range. + if (R.isTokenRange()) + EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); + } else { + EndColNo = CaretLine.size(); + } + } + + assert(StartColNo <= EndColNo && "Invalid range!"); + + // Check that a token range does not highlight only whitespace. + if (R.isTokenRange()) { + // Pick the first non-whitespace column. + while (StartColNo < map.getSourceLine().size() && + (map.getSourceLine()[StartColNo] == ' ' || + map.getSourceLine()[StartColNo] == '\t')) + StartColNo = map.startOfNextColumn(StartColNo); + + // Pick the last non-whitespace column. + if (EndColNo > map.getSourceLine().size()) + EndColNo = map.getSourceLine().size(); + while (EndColNo && + (map.getSourceLine()[EndColNo-1] == ' ' || + map.getSourceLine()[EndColNo-1] == '\t')) + EndColNo = map.startOfPreviousColumn(EndColNo); + + // If the start/end passed each other, then we are trying to highlight a + // range that just exists in whitespace, which must be some sort of other + // bug. + assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); + } + + assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); + assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); + + // Fill the range with ~'s. + StartColNo = map.byteToContainingColumn(StartColNo); + EndColNo = map.byteToContainingColumn(EndColNo); + + assert(StartColNo <= EndColNo && "Invalid range!"); + if (CaretLine.size() < EndColNo) + CaretLine.resize(EndColNo,' '); + std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); +} + +static std::string buildFixItInsertionLine(unsigned LineNo, + const SourceColumnMap &map, + ArrayRef<FixItHint> Hints, + const SourceManager &SM, + const DiagnosticOptions *DiagOpts) { + std::string FixItInsertionLine; + if (Hints.empty() || !DiagOpts->ShowFixits) + return FixItInsertionLine; + unsigned PrevHintEndCol = 0; + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (!I->CodeToInsert.empty()) { + // We have an insertion hint. Determine whether the inserted + // code contains no newlines and is on the same line as the caret. + std::pair<FileID, unsigned> HintLocInfo + = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); + if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && + StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) { + // Insert the new code into the line just below the code + // that the user wrote. + // Note: When modifying this function, be very careful about what is a + // "column" (printed width, platform-dependent) and what is a + // "byte offset" (SourceManager "column"). + unsigned HintByteOffset + = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; + + // The hint must start inside the source or right at the end + assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); + unsigned HintCol = map.byteToContainingColumn(HintByteOffset); + + // If we inserted a long previous hint, push this one forwards, and add + // an extra space to show that this is not part of the previous + // completion. This is sort of the best we can do when two hints appear + // to overlap. + // + // Note that if this hint is located immediately after the previous + // hint, no space will be added, since the location is more important. + if (HintCol < PrevHintEndCol) + HintCol = PrevHintEndCol + 1; + + // This should NOT use HintByteOffset, because the source might have + // Unicode characters in earlier columns. + unsigned NewFixItLineSize = FixItInsertionLine.size() + + (HintCol - PrevHintEndCol) + I->CodeToInsert.size(); + if (NewFixItLineSize > FixItInsertionLine.size()) + FixItInsertionLine.resize(NewFixItLineSize, ' '); + + std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), + FixItInsertionLine.end() - I->CodeToInsert.size()); + + PrevHintEndCol = + HintCol + llvm::sys::locale::columnWidth(I->CodeToInsert); + } else { + FixItInsertionLine.clear(); + break; + } + } + } + + expandTabs(FixItInsertionLine, DiagOpts->TabStop); + + return FixItInsertionLine; +} + +/// \brief Emit a code snippet and caret line. +/// +/// This routine emits a single line's code snippet and caret line.. +/// +/// \param Loc The location for the caret. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +void TextDiagnostic::emitSnippetAndCaret( + SourceLocation Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + assert(!Loc.isInvalid() && "must have a valid source location here"); + assert(Loc.isFileID() && "must have a file location here"); + + // If caret diagnostics are enabled and we have location, we want to + // emit the caret. However, we only do this if the location moved + // from the last diagnostic, if the last diagnostic was a note that + // was part of a different warning or error diagnostic, or if the + // diagnostic has ranges. We don't want to emit the same caret + // multiple times if one loc has multiple diagnostics. + if (!DiagOpts->ShowCarets) + return; + if (Loc == LastLoc && Ranges.empty() && Hints.empty() && + (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) + return; + + // Decompose the location into a FID/Offset pair. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + // Get information about the buffer it points into. + bool Invalid = false; + const char *BufStart = SM.getBufferData(FID, &Invalid).data(); + if (Invalid) + return; + + unsigned LineNo = SM.getLineNumber(FID, FileOffset); + unsigned ColNo = SM.getColumnNumber(FID, FileOffset); + + // Arbitrarily stop showing snippets when the line is too long. + static const size_t MaxLineLengthToPrint = 4096; + if (ColNo > MaxLineLengthToPrint) + return; + + // Rewind from the current position to the start of the line. + const char *TokPtr = BufStart+FileOffset; + const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. + + // Compute the line end. Scan forward from the error position to the end of + // the line. + const char *LineEnd = TokPtr; + while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') + ++LineEnd; + + // Arbitrarily stop showing snippets when the line is too long. + if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) + return; + + // Copy the line of code into an std::string for ease of manipulation. + std::string SourceLine(LineStart, LineEnd); + + // Build the byte to column map. + const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); + + // Create a line for the caret that is filled with spaces that is the same + // number of columns as the line of source code. + std::string CaretLine(sourceColMap.columns(), ' '); + + // Highlight all of the characters covered by Ranges with ~ characters. + for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) + highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); + + // Next, insert the caret itself. + ColNo = sourceColMap.byteToContainingColumn(ColNo-1); + if (CaretLine.size()<ColNo+1) + CaretLine.resize(ColNo+1, ' '); + CaretLine[ColNo] = '^'; + + std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, + sourceColMap, + Hints, SM, + DiagOpts.get()); + + // If the source line is too long for our terminal, select only the + // "interesting" source region within that line. + unsigned Columns = DiagOpts->MessageLength; + if (Columns) + selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, + Columns, sourceColMap); + + // If we are in -fdiagnostics-print-source-range-info mode, we are trying + // to produce easily machine parsable output. Add a space before the + // source line and the caret to make it trivial to tell the main diagnostic + // line from what the user is intended to see. + if (DiagOpts->ShowSourceRanges) { + SourceLine = ' ' + SourceLine; + CaretLine = ' ' + CaretLine; + } + + // Finally, remove any blank spaces from the end of CaretLine. + while (CaretLine[CaretLine.size()-1] == ' ') + CaretLine.erase(CaretLine.end()-1); + + // Emit what we have computed. + emitSnippet(SourceLine); + + if (DiagOpts->ShowColors) + OS.changeColor(caretColor, true); + OS << CaretLine << '\n'; + if (DiagOpts->ShowColors) + OS.resetColor(); + + if (!FixItInsertionLine.empty()) { + if (DiagOpts->ShowColors) + // Print fixit line in color + OS.changeColor(fixitColor, false); + if (DiagOpts->ShowSourceRanges) + OS << ' '; + OS << FixItInsertionLine << '\n'; + if (DiagOpts->ShowColors) + OS.resetColor(); + } + + // Print out any parseable fixit information requested by the options. + emitParseableFixits(Hints, SM); +} + +void TextDiagnostic::emitSnippet(StringRef line) { + if (line.empty()) + return; + + size_t i = 0; + + std::string to_print; + bool print_reversed = false; + + while (i<line.size()) { + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(line, &i, DiagOpts->TabStop); + bool was_printable = res.second; + + if (DiagOpts->ShowColors && was_printable == print_reversed) { + if (print_reversed) + OS.reverseColor(); + OS << to_print; + to_print.clear(); + if (DiagOpts->ShowColors) + OS.resetColor(); + } + + print_reversed = !was_printable; + to_print += res.first.str(); + } + + if (print_reversed && DiagOpts->ShowColors) + OS.reverseColor(); + OS << to_print; + if (print_reversed && DiagOpts->ShowColors) + OS.resetColor(); + + OS << '\n'; +} + +void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + if (!DiagOpts->ShowParseableFixits) + return; + + // We follow FixItRewriter's example in not (yet) handling + // fix-its in macros. + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (I->RemoveRange.isInvalid() || + I->RemoveRange.getBegin().isMacroID() || + I->RemoveRange.getEnd().isMacroID()) + return; + } + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + SourceLocation BLoc = I->RemoveRange.getBegin(); + SourceLocation ELoc = I->RemoveRange.getEnd(); + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); + + // Adjust for token ranges. + if (I->RemoveRange.isTokenRange()) + EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); + + // We specifically do not do word-wrapping or tab-expansion here, + // because this is supposed to be easy to parse. + PresumedLoc PLoc = SM.getPresumedLoc(BLoc); + if (PLoc.isInvalid()) + break; + + OS << "fix-it:\""; + OS.write_escaped(PLoc.getFilename()); + OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) + << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) + << '-' << SM.getLineNumber(EInfo.first, EInfo.second) + << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) + << "}:\""; + OS.write_escaped(I->CodeToInsert); + OS << "\"\n"; + } +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp new file mode 100644 index 0000000..9c6bebb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -0,0 +1,63 @@ +//===--- TextDiagnosticBuffer.cpp - Buffer Text Diagnostics ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a concrete diagnostic client, which buffers the diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +using namespace clang; + +/// HandleDiagnostic - Store the errors, warnings, and notes that are +/// reported. +/// +void TextDiagnosticBuffer::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + SmallString<100> Buf; + Info.FormatDiagnostic(Buf); + switch (Level) { + default: llvm_unreachable( + "Diagnostic not handled during diagnostic buffering!"); + case DiagnosticsEngine::Note: + Notes.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case DiagnosticsEngine::Warning: + Warnings.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case DiagnosticsEngine::Remark: + Remarks.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + Errors.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + } +} + +void TextDiagnosticBuffer::FlushDiagnostics(DiagnosticsEngine &Diags) const { + // FIXME: Flush the diagnostics in order. + for (const_iterator it = err_begin(), ie = err_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0")) + << it->second; + for (const_iterator it = warn_begin(), ie = warn_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Warning, "%0")) + << it->second; + for (const_iterator it = remark_begin(), ie = remark_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Remark, "%0")) + << it->second; + for (const_iterator it = note_begin(), ie = note_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0")) + << it->second; +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp new file mode 100644 index 0000000..66b46b7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -0,0 +1,161 @@ +//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This diagnostic client prints out their diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/TextDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +using namespace clang; + +TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, + DiagnosticOptions *diags, + bool _OwnsOutputStream) + : OS(os), DiagOpts(diags), + OwnsOutputStream(_OwnsOutputStream) { +} + +TextDiagnosticPrinter::~TextDiagnosticPrinter() { + if (OwnsOutputStream) + delete &OS; +} + +void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + // Build the TextDiagnostic utility. + TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts)); +} + +void TextDiagnosticPrinter::EndSourceFile() { + TextDiag.reset(); +} + +/// \brief Print any diagnostic option information to a raw_ostream. +/// +/// This implements all of the logic for adding diagnostic options to a message +/// (via OS). Each relevant option is comma separated and all are enclosed in +/// the standard bracketing: " [...]". +static void printDiagnosticOptions(raw_ostream &OS, + DiagnosticsEngine::Level Level, + const Diagnostic &Info, + const DiagnosticOptions &DiagOpts) { + bool Started = false; + if (DiagOpts.ShowOptionNames) { + // Handle special cases for non-warnings early. + if (Info.getID() == diag::fatal_too_many_errors) { + OS << " [-ferror-limit=]"; + return; + } + + // The code below is somewhat fragile because we are essentially trying to + // report to the user what happened by inferring what the diagnostic engine + // did. Eventually it might make more sense to have the diagnostic engine + // include some "why" information in the diagnostic. + + // If this is a warning which has been mapped to an error by the user (as + // inferred by checking whether the default mapping is to an error) then + // flag it as such. Note that diagnostics could also have been mapped by a + // pragma, but we don't currently have a way to distinguish this. + if (Level == DiagnosticsEngine::Error && + DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) && + !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { + OS << " [-Werror"; + Started = true; + } + + StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); + if (!Opt.empty()) { + OS << (Started ? "," : " [") + << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; + StringRef OptValue = Info.getDiags()->getFlagValue(); + if (!OptValue.empty()) + OS << "=" << OptValue; + Started = true; + } + } + + // If the user wants to see category information, include it too. + if (DiagOpts.ShowCategories) { + unsigned DiagCategory = + DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); + if (DiagCategory) { + OS << (Started ? "," : " ["); + Started = true; + if (DiagOpts.ShowCategories == 1) + OS << DiagCategory; + else { + assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value"); + OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory); + } + } + } + if (Started) + OS << ']'; +} + +void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Render the diagnostic message into a temporary buffer eagerly. We'll use + // this later as we print out the diagnostic to the terminal. + SmallString<100> OutStr; + Info.FormatDiagnostic(OutStr); + + llvm::raw_svector_ostream DiagMessageStream(OutStr); + printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); + + // Keeps track of the starting position of the location + // information (e.g., "foo.c:10:4:") that precedes the error + // message. We use this information to determine how long the + // file+line+column number prefix is. + uint64_t StartOfLocationInfo = OS.tell(); + + if (!Prefix.empty()) + OS << Prefix << ": "; + + // Use a dedicated, simpler path for diagnostics without a valid location. + // This is important as if the location is missing, we may be emitting + // diagnostics in a context that lacks language options, a source manager, or + // other infrastructure necessary when emitting more rich diagnostics. + if (!Info.getLocation().isValid()) { + TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); + TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), + OS.tell() - StartOfLocationInfo, + DiagOpts->MessageLength, + DiagOpts->ShowColors); + OS.flush(); + return; + } + + // Assert that the rest of our infrastructure is setup properly. + assert(DiagOpts && "Unexpected diagnostic without options set"); + assert(Info.hasSourceManager() && + "Unexpected diagnostic with no source manager"); + assert(TextDiag && "Unexpected diagnostic outside source file processing"); + + TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), + Info.getRanges(), + Info.getFixItHints(), + &Info.getSourceManager()); + + OS.flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp new file mode 100644 index 0000000..3ff6b18 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -0,0 +1,906 @@ +//===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a concrete diagnostic client, which buffers the diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +typedef VerifyDiagnosticConsumer::Directive Directive; +typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; +typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; + +VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) + : Diags(Diags_), + PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()), + Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr), + LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0), + Status(HasNoDirectives) +{ + if (Diags.hasSourceManager()) + setSourceManager(Diags.getSourceManager()); +} + +VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { + assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); + assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); + SrcManager = nullptr; + CheckDiagnostics(); + Diags.takeClient().release(); +} + +#ifndef NDEBUG +namespace { +class VerifyFileTracker : public PPCallbacks { + VerifyDiagnosticConsumer &Verify; + SourceManager &SM; + +public: + VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) + : Verify(Verify), SM(SM) { } + + /// \brief Hook into the preprocessor and update the list of parsed + /// files when the preprocessor indicates a new file is entered. + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), + VerifyDiagnosticConsumer::IsParsed); + } +}; +} // End anonymous namespace. +#endif + +// DiagnosticConsumer interface. + +void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP) { + // Attach comment handler on first invocation. + if (++ActiveSourceFiles == 1) { + if (PP) { + CurrentPreprocessor = PP; + this->LangOpts = &LangOpts; + setSourceManager(PP->getSourceManager()); + const_cast<Preprocessor*>(PP)->addCommentHandler(this); +#ifndef NDEBUG + // Debug build tracks parsed files. + const_cast<Preprocessor*>(PP)->addPPCallbacks( + llvm::make_unique<VerifyFileTracker>(*this, *SrcManager)); +#endif + } + } + + assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); + PrimaryClient->BeginSourceFile(LangOpts, PP); +} + +void VerifyDiagnosticConsumer::EndSourceFile() { + assert(ActiveSourceFiles && "No active source files!"); + PrimaryClient->EndSourceFile(); + + // Detach comment handler once last active source file completed. + if (--ActiveSourceFiles == 0) { + if (CurrentPreprocessor) + const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); + + // Check diagnostics once last file completed. + CheckDiagnostics(); + CurrentPreprocessor = nullptr; + LangOpts = nullptr; + } +} + +void VerifyDiagnosticConsumer::HandleDiagnostic( + DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { + if (Info.hasSourceManager()) { + // If this diagnostic is for a different source manager, ignore it. + if (SrcManager && &Info.getSourceManager() != SrcManager) + return; + + setSourceManager(Info.getSourceManager()); + } + +#ifndef NDEBUG + // Debug build tracks unparsed files for possible + // unparsed expected-* directives. + if (SrcManager) { + SourceLocation Loc = Info.getLocation(); + if (Loc.isValid()) { + ParsedStatus PS = IsUnparsed; + + Loc = SrcManager->getExpansionLoc(Loc); + FileID FID = SrcManager->getFileID(Loc); + + const FileEntry *FE = SrcManager->getFileEntryForID(FID); + if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { + // If the file is a modules header file it shall not be parsed + // for expected-* directives. + HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); + if (HS.findModuleForHeader(FE)) + PS = IsUnparsedNoDirectives; + } + + UpdateParsedFileStatus(*SrcManager, FID, PS); + } + } +#endif + + // Send the diagnostic to the buffer, we will check it once we reach the end + // of the source file (or are destructed). + Buffer->HandleDiagnostic(DiagLevel, Info); +} + +//===----------------------------------------------------------------------===// +// Checking diagnostics implementation. +//===----------------------------------------------------------------------===// + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +namespace { + +/// StandardDirective - Directive with string matching. +/// +class StandardDirective : public Directive { +public: + StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, unsigned Min, + unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { } + + bool isValid(std::string &Error) override { + // all strings are considered valid; even empty ones + return true; + } + + bool match(StringRef S) override { + return S.find(Text) != StringRef::npos; + } +}; + +/// RegexDirective - Directive with regular-expression matching. +/// +class RegexDirective : public Directive { +public: + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, + StringRef RegexStr) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), + Regex(RegexStr) { } + + bool isValid(std::string &Error) override { + if (Regex.isValid(Error)) + return true; + return false; + } + + bool match(StringRef S) override { + return Regex.match(S); + } + +private: + llvm::Regex Regex; +}; + +class ParseHelper +{ +public: + ParseHelper(StringRef S) + : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {} + + // Return true if string literal is next. + bool Next(StringRef S) { + P = C; + PEnd = C + S.size(); + if (PEnd > End) + return false; + return !memcmp(P, S.data(), S.size()); + } + + // Return true if number is next. + // Output N only if number is next. + bool Next(unsigned &N) { + unsigned TMP = 0; + P = C; + for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { + TMP *= 10; + TMP += P[0] - '0'; + } + if (P == C) + return false; + PEnd = P; + N = TMP; + return true; + } + + // Return true if string literal is found. + // When true, P marks begin-position of S in content. + bool Search(StringRef S, bool EnsureStartOfWord = false) { + do { + P = std::search(C, End, S.begin(), S.end()); + PEnd = P + S.size(); + if (P == End) + break; + if (!EnsureStartOfWord + // Check if string literal starts a new word. + || P == Begin || isWhitespace(P[-1]) + // Or it could be preceded by the start of a comment. + || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') + && P[-2] == '/')) + return true; + // Otherwise, skip and search again. + } while (Advance()); + return false; + } + + // Return true if a CloseBrace that closes the OpenBrace at the current nest + // level is found. When true, P marks begin-position of CloseBrace. + bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { + unsigned Depth = 1; + P = C; + while (P < End) { + StringRef S(P, End - P); + if (S.startswith(OpenBrace)) { + ++Depth; + P += OpenBrace.size(); + } else if (S.startswith(CloseBrace)) { + --Depth; + if (Depth == 0) { + PEnd = P + CloseBrace.size(); + return true; + } + P += CloseBrace.size(); + } else { + ++P; + } + } + return false; + } + + // Advance 1-past previous next/search. + // Behavior is undefined if previous next/search failed. + bool Advance() { + C = PEnd; + return C < End; + } + + // Skip zero or more whitespace. + void SkipWhitespace() { + for (; C < End && isWhitespace(*C); ++C) + ; + } + + // Return true if EOF reached. + bool Done() { + return !(C < End); + } + + const char * const Begin; // beginning of expected content + const char * const End; // end of expected content (1-past) + const char *C; // position of next char in content + const char *P; + +private: + const char *PEnd; // previous next/search subject end (1-past) +}; + +} // namespace anonymous + +/// ParseDirective - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in the appropriate directive list. +/// +/// Returns true if any valid directives were found. +static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, + Preprocessor *PP, SourceLocation Pos, + VerifyDiagnosticConsumer::DirectiveStatus &Status) { + DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); + + // A single comment may contain multiple directives. + bool FoundDirective = false; + for (ParseHelper PH(S); !PH.Done();) { + // Search for token: expected + if (!PH.Search("expected", true)) + break; + PH.Advance(); + + // Next token: - + if (!PH.Next("-")) + continue; + PH.Advance(); + + // Next token: { error | warning | note } + DirectiveList *DL = nullptr; + if (PH.Next("error")) + DL = ED ? &ED->Errors : nullptr; + else if (PH.Next("warning")) + DL = ED ? &ED->Warnings : nullptr; + else if (PH.Next("remark")) + DL = ED ? &ED->Remarks : nullptr; + else if (PH.Next("note")) + DL = ED ? &ED->Notes : nullptr; + else if (PH.Next("no-diagnostics")) { + if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) + Diags.Report(Pos, diag::err_verify_invalid_no_diags) + << /*IsExpectedNoDiagnostics=*/true; + else + Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; + continue; + } else + continue; + PH.Advance(); + + if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { + Diags.Report(Pos, diag::err_verify_invalid_no_diags) + << /*IsExpectedNoDiagnostics=*/false; + continue; + } + Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; + + // If a directive has been found but we're not interested + // in storing the directive information, return now. + if (!DL) + return true; + + // Default directive kind. + bool RegexKind = false; + const char* KindStr = "string"; + + // Next optional token: - + if (PH.Next("-re")) { + PH.Advance(); + RegexKind = true; + KindStr = "regex"; + } + + // Next optional token: @ + SourceLocation ExpectedLoc; + bool MatchAnyLine = false; + if (!PH.Next("@")) { + ExpectedLoc = Pos; + } else { + PH.Advance(); + unsigned Line = 0; + bool FoundPlus = PH.Next("+"); + if (FoundPlus || PH.Next("-")) { + // Relative to current line. + PH.Advance(); + bool Invalid = false; + unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); + if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { + if (FoundPlus) ExpectedLine += Line; + else ExpectedLine -= Line; + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); + } + } else if (PH.Next(Line)) { + // Absolute line number. + if (Line > 0) + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); + } else if (PP && PH.Search(":")) { + // Specific source file. + StringRef Filename(PH.C, PH.P-PH.C); + PH.Advance(); + + // Lookup file via Preprocessor, like a #include. + const DirectoryLookup *CurDir; + const FileEntry *FE = + PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, + nullptr, nullptr, nullptr); + if (!FE) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_file) << Filename << KindStr; + continue; + } + + if (SM.translateFile(FE).isInvalid()) + SM.createFileID(FE, Pos, SrcMgr::C_User); + + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); + else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); + } + } + + if (ExpectedLoc.isInvalid()) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_line) << KindStr; + continue; + } + PH.Advance(); + } + + // Skip optional whitespace. + PH.SkipWhitespace(); + + // Next optional token: positive integer or a '+'. + unsigned Min = 1; + unsigned Max = 1; + if (PH.Next(Min)) { + PH.Advance(); + // A positive integer can be followed by a '+' meaning min + // or more, or by a '-' meaning a range from min to max. + if (PH.Next("+")) { + Max = Directive::MaxCount; + PH.Advance(); + } else if (PH.Next("-")) { + PH.Advance(); + if (!PH.Next(Max) || Max < Min) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_invalid_range) << KindStr; + continue; + } + PH.Advance(); + } else { + Max = Min; + } + } else if (PH.Next("+")) { + // '+' on its own means "1 or more". + Max = Directive::MaxCount; + PH.Advance(); + } + + // Skip optional whitespace. + PH.SkipWhitespace(); + + // Next token: {{ + if (!PH.Next("{{")) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_start) << KindStr; + continue; + } + PH.Advance(); + const char* const ContentBegin = PH.C; // mark content begin + + // Search for token: }} + if (!PH.SearchClosingBrace("{{", "}}")) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_end) << KindStr; + continue; + } + const char* const ContentEnd = PH.P; // mark content end + PH.Advance(); + + // Build directive text; convert \n to newlines. + std::string Text; + StringRef NewlineStr = "\\n"; + StringRef Content(ContentBegin, ContentEnd-ContentBegin); + size_t CPos = 0; + size_t FPos; + while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { + Text += Content.substr(CPos, FPos-CPos); + Text += '\n'; + CPos = FPos + NewlineStr.size(); + } + if (Text.empty()) + Text.assign(ContentBegin, ContentEnd); + + // Check that regex directives contain at least one regex. + if (RegexKind && Text.find("{{") == StringRef::npos) { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_missing_regex) << Text; + return false; + } + + // Construct new directive. + std::unique_ptr<Directive> D = Directive::create( + RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); + + std::string Error; + if (D->isValid(Error)) { + DL->push_back(std::move(D)); + FoundDirective = true; + } else { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_invalid_content) + << KindStr << Error; + } + } + + return FoundDirective; +} + +/// HandleComment - Hook into the preprocessor and extract comments containing +/// expected errors and warnings. +bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, + SourceRange Comment) { + SourceManager &SM = PP.getSourceManager(); + + // If this comment is for a different source manager, ignore it. + if (SrcManager && &SM != SrcManager) + return false; + + SourceLocation CommentBegin = Comment.getBegin(); + + const char *CommentRaw = SM.getCharacterData(CommentBegin); + StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); + + if (C.empty()) + return false; + + // Fold any "\<EOL>" sequences + size_t loc = C.find('\\'); + if (loc == StringRef::npos) { + ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); + return false; + } + + std::string C2; + C2.reserve(C.size()); + + for (size_t last = 0;; loc = C.find('\\', last)) { + if (loc == StringRef::npos || loc == C.size()) { + C2 += C.substr(last); + break; + } + C2 += C.substr(last, loc-last); + last = loc + 1; + + if (C[last] == '\n' || C[last] == '\r') { + ++last; + + // Escape \r\n or \n\r, but not \n\n. + if (last < C.size()) + if (C[last] == '\n' || C[last] == '\r') + if (C[last] != C[last-1]) + ++last; + } else { + // This was just a normal backslash. + C2 += '\\'; + } + } + + if (!C2.empty()) + ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); + return false; +} + +#ifndef NDEBUG +/// \brief Lex the specified source file to determine whether it contains +/// any expected-* directives. As a Lexer is used rather than a full-blown +/// Preprocessor, directives inside skipped #if blocks will still be found. +/// +/// \return true if any directives were found. +static bool findDirectives(SourceManager &SM, FileID FID, + const LangOptions &LangOpts) { + // Create a raw lexer to pull all the comments out of FID. + if (FID.isInvalid()) + return false; + + // Create a lexer to lex all the tokens of the main file in raw mode. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer RawLex(FID, FromFile, SM, LangOpts); + + // Return comments as tokens, this is how we find expected diagnostics. + RawLex.SetCommentRetentionState(true); + + Token Tok; + Tok.setKind(tok::comment); + VerifyDiagnosticConsumer::DirectiveStatus Status = + VerifyDiagnosticConsumer::HasNoDirectives; + while (Tok.isNot(tok::eof)) { + RawLex.LexFromRawLexer(Tok); + if (!Tok.is(tok::comment)) continue; + + std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); + if (Comment.empty()) continue; + + // Find first directive. + if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), + Status)) + return true; + } + return false; +} +#endif // !NDEBUG + +/// \brief Takes a list of diagnostics that have been generated but not matched +/// by an expected-* directive and produces a diagnostic to the user from this. +static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Kind) { + if (diag_begin == diag_end) return 0; + + SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { + if (I->first.isInvalid() || !SourceMgr) + OS << "\n (frontend)"; + else { + OS << "\n "; + if (const FileEntry *File = SourceMgr->getFileEntryForID( + SourceMgr->getFileID(I->first))) + OS << " File " << File->getName(); + OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); + } + OS << ": " << I->second; + } + + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/true << OS.str(); + return std::distance(diag_begin, diag_end); +} + +/// \brief Takes a list of diagnostics that were expected to have been generated +/// but were not and produces a diagnostic to the user from this. +static unsigned PrintExpected(DiagnosticsEngine &Diags, + SourceManager &SourceMgr, + std::vector<Directive *> &DL, const char *Kind) { + if (DL.empty()) + return 0; + + SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (auto *DirPtr : DL) { + Directive &D = *DirPtr; + OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc); + if (D.MatchAnyLine) + OS << " Line *"; + else + OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + if (D.DirectiveLoc != D.DiagnosticLoc) + OS << " (directive at " + << SourceMgr.getFilename(D.DirectiveLoc) << ':' + << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; + OS << ": " << D.Text; + } + + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/false << OS.str(); + return DL.size(); +} + +/// \brief Determine whether two source locations come from the same file. +static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc) { + while (DiagnosticLoc.isMacroID()) + DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); + + if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) + return true; + + const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); + if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) + return true; + + return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); +} + +/// CheckLists - Compare expected to seen diagnostic lists and return the +/// the difference between them. +/// +static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + const char *Label, + DirectiveList &Left, + const_diag_iterator d2_begin, + const_diag_iterator d2_end) { + std::vector<Directive *> LeftOnly; + DiagList Right(d2_begin, d2_end); + + for (auto &Owner : Left) { + Directive &D = *Owner; + unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + + for (unsigned i = 0; i < D.Max; ++i) { + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + if (!D.MatchAnyLine) { + unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); + if (LineNo1 != LineNo2) + continue; + } + + if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) + continue; + + const std::string &RightText = II->second; + if (D.match(RightText)) + break; + } + if (II == IE) { + // Not found. + if (i >= D.Min) break; + LeftOnly.push_back(&D); + } else { + // Found. The same cannot be found twice. + Right.erase(II); + } + } + } + // Now all that's left in Right are those that were not matched. + unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); + num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); + return num; +} + +/// 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 unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + const TextDiagnosticBuffer &Buffer, + ExpectedData &ED) { + // 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 + unsigned NumProblems = 0; + + // See if there are error mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, + Buffer.err_begin(), Buffer.err_end()); + + // See if there are warning mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, + Buffer.warn_begin(), Buffer.warn_end()); + + // See if there are remark mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, + Buffer.remark_begin(), Buffer.remark_end()); + + // See if there are note mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, + Buffer.note_begin(), Buffer.note_end()); + + return NumProblems; +} + +void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, + FileID FID, + ParsedStatus PS) { + // Check SourceManager hasn't changed. + setSourceManager(SM); + +#ifndef NDEBUG + if (FID.isInvalid()) + return; + + const FileEntry *FE = SM.getFileEntryForID(FID); + + if (PS == IsParsed) { + // Move the FileID from the unparsed set to the parsed set. + UnparsedFiles.erase(FID); + ParsedFiles.insert(std::make_pair(FID, FE)); + } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { + // Add the FileID to the unparsed set if we haven't seen it before. + + // Check for directives. + bool FoundDirectives; + if (PS == IsUnparsedNoDirectives) + FoundDirectives = false; + else + FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); + + // Add the FileID to the unparsed set. + UnparsedFiles.insert(std::make_pair(FID, + UnparsedFileStatus(FE, FoundDirectives))); + } +#endif +} + +void VerifyDiagnosticConsumer::CheckDiagnostics() { + // Ensure any diagnostics go to the primary client. + DiagnosticConsumer *CurClient = Diags.getClient(); + std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient(); + Diags.setClient(PrimaryClient, false); + +#ifndef NDEBUG + // In a debug build, scan through any files that may have been missed + // during parsing and issue a fatal error if directives are contained + // within these files. If a fatal error occurs, this suggests that + // this file is being parsed separately from the main file, in which + // case consider moving the directives to the correct place, if this + // is applicable. + if (UnparsedFiles.size() > 0) { + // Generate a cache of parsed FileEntry pointers for alias lookups. + llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; + for (ParsedFilesMap::iterator I = ParsedFiles.begin(), + End = ParsedFiles.end(); I != End; ++I) { + if (const FileEntry *FE = I->second) + ParsedFileCache.insert(FE); + } + + // Iterate through list of unparsed files. + for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), + End = UnparsedFiles.end(); I != End; ++I) { + const UnparsedFileStatus &Status = I->second; + const FileEntry *FE = Status.getFile(); + + // Skip files that have been parsed via an alias. + if (FE && ParsedFileCache.count(FE)) + continue; + + // Report a fatal error if this file contained directives. + if (Status.foundDirectives()) { + llvm::report_fatal_error(Twine("-verify directives found after rather" + " than during normal parsing of ", + StringRef(FE ? FE->getName() : "(unknown)"))); + } + } + + // UnparsedFiles has been processed now, so clear it. + UnparsedFiles.clear(); + } +#endif // !NDEBUG + + if (SrcManager) { + // Produce an error if no expected-* directives could be found in the + // source file(s) processed. + if (Status == HasNoDirectives) { + Diags.Report(diag::err_verify_no_directives).setForceEmit(); + ++NumErrors; + Status = HasNoDirectivesReported; + } + + // Check that the expected diagnostics occurred. + NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); + } else { + NumErrors += (PrintUnexpected(Diags, nullptr, Buffer->err_begin(), + Buffer->err_end(), "error") + + PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), + Buffer->warn_end(), "warn") + + PrintUnexpected(Diags, nullptr, Buffer->note_begin(), + Buffer->note_end(), "note")); + } + + Diags.setClient(CurClient, Owner.release() != nullptr); + + // Reset the buffer, we have processed all the diagnostics in it. + Buffer.reset(new TextDiagnosticBuffer()); + ED.Reset(); +} + +std::unique_ptr<Directive> Directive::create(bool RegexKind, + SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) { + if (!RegexKind) + return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, + MatchAnyLine, Text, Min, Max); + + // Parse the directive into a regular expression. + std::string RegexStr; + StringRef S = Text; + while (!S.empty()) { + if (S.startswith("{{")) { + S = S.drop_front(2); + size_t RegexMatchLength = S.find("}}"); + assert(RegexMatchLength != StringRef::npos); + // Append the regex, enclosed in parentheses. + RegexStr += "("; + RegexStr.append(S.data(), RegexMatchLength); + RegexStr += ")"; + S = S.drop_front(RegexMatchLength + 2); + } else { + size_t VerbatimMatchLength = S.find("{{"); + if (VerbatimMatchLength == StringRef::npos) + VerbatimMatchLength = S.size(); + // Escape and append the fixed string. + RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); + S = S.drop_front(VerbatimMatchLength); + } + } + + return llvm::make_unique<RegexDirective>( + DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); +} |