//===--- 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/Frontend/PCHReader.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/Support/Compiler.h"

using namespace clang;

ASTUnit::ASTUnit() { }
ASTUnit::~ASTUnit() { }

namespace {

/// \brief Gathers information from PCHReader that will be used to initialize
/// a Preprocessor.
class VISIBILITY_HIDDEN PCHInfoCollector : public PCHReaderListener {
  LangOptions &LangOpt;
  HeaderSearch &HSI;
  std::string &TargetTriple;
  std::string &Predefines;
  unsigned &Counter;
  
  unsigned NumHeaderInfos;
  
public:
  PCHInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI,
                   std::string &TargetTriple, std::string &Predefines,
                   unsigned &Counter)
    : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple),
      Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {}
  
  virtual bool ReadLanguageOptions(const LangOptions &LangOpts) {
    LangOpt = LangOpts;
    return false;
  }
  
  virtual bool ReadTargetTriple(const std::string &Triple) {
    TargetTriple = Triple;
    return false;
  }
  
  virtual bool ReadPredefinesBuffer(const char *PCHPredef, 
                                    unsigned PCHPredefLen,
                                    FileID PCHBufferID,
                                    std::string &SuggestedPredefines) {
    Predefines = PCHPredef;
    return false;
  }
  
  virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI) {
    HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++);
  }
  
  virtual void ReadCounter(unsigned Value) {
    Counter = Value;
  }
};

} // anonymous namespace


ASTUnit *ASTUnit::LoadFromPCHFile(const std::string &Filename,
                                  FileManager &FileMgr,
                                  std::string *ErrMsg) {
  
  llvm::OwningPtr<ASTUnit> AST(new ASTUnit());

  AST->DiagClient.reset(new TextDiagnosticBuffer());
  AST->Diags.reset(new Diagnostic(AST->DiagClient.get()));

  AST->HeaderInfo.reset(new HeaderSearch(FileMgr));
  AST->SourceMgr.reset(new SourceManager());
  
  Diagnostic &Diags = *AST->Diags.get();
  SourceManager &SourceMgr = *AST->SourceMgr.get();

  // Gather Info for preprocessor construction later on.
  
  LangOptions LangInfo;
  HeaderSearch &HeaderInfo = *AST->HeaderInfo.get();
  std::string TargetTriple;
  std::string Predefines;
  unsigned Counter;

  llvm::OwningPtr<PCHReader> Reader;
  llvm::OwningPtr<ExternalASTSource> Source;

  Reader.reset(new PCHReader(SourceMgr, FileMgr, Diags));
  Reader->setListener(new PCHInfoCollector(LangInfo, HeaderInfo, TargetTriple,
                                           Predefines, Counter));

  switch (Reader->ReadPCH(Filename)) {
  case PCHReader::Success:
    break;
    
  case PCHReader::Failure:
    // Unrecoverable failure: don't even try to process the input
    // file.
    if (ErrMsg)
      *ErrMsg = "Could not load PCH file";
    return NULL;

  case PCHReader::IgnorePCH:
    assert(0 && "Is there a validation that should not have happened ?");
  }
  
  // PCH loaded successfully. Now create the preprocessor.
  
  // Get information about the target being compiled for.
  AST->Target.reset(TargetInfo::CreateTargetInfo(TargetTriple));
  AST->PP.reset(new Preprocessor(Diags, LangInfo, *AST->Target.get(),
                                 SourceMgr, HeaderInfo));
  Preprocessor &PP = *AST->PP.get();

  PP.setPredefines(Predefines);
  PP.setCounterValue(Counter);
  Reader->setPreprocessor(PP);
  
  // Create and initialize the ASTContext.

  AST->Ctx.reset(new ASTContext(LangInfo,
                                SourceMgr,
                                *AST->Target.get(),
                                PP.getIdentifierTable(),
                                PP.getSelectorTable(),
                                PP.getBuiltinInfo(),
                                /* FreeMemory = */ true,
                                /* size_reserve = */0));
  ASTContext &Context = *AST->Ctx.get();
  
  Reader->InitializeContext(Context);
  
  // Attach the PCH reader to the AST context as an external AST
  // source, so that declarations will be deserialized from the
  // PCH file as needed.
  Source.reset(Reader.take());
  Context.setExternalSource(Source);

  return AST.take(); 
}
