[libclang] Improve AST serialization done by ASTUnit::Save().

The ASTUnit needs to initialize an ASTWriter at the beginning of
parsing to fully handle serialization of a translation unit that
imports modules. Do this by introducing an option to enable it, which
corresponds to CXTranslationUnit_ForSerialization on the C API side.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@165717 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 6a4f0eb..9954705 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -180,6 +180,14 @@
   CleanPreambleFile();
 }
 
+struct ASTUnit::ASTWriterData {
+  SmallString<128> Buffer;
+  llvm::BitstreamWriter Stream;
+  ASTWriter Writer;
+
+  ASTWriterData() : Stream(Buffer), Writer(Stream) { }
+};
+
 void ASTUnit::clearFileLevelDecls() {
   for (FileDeclsTy::iterator
          I = FileDecls.begin(), E = FileDecls.end(); I != E; ++I)
@@ -514,29 +522,26 @@
 
   virtual bool ReadLanguageOptions(const serialization::ModuleFile &M,
                                    const LangOptions &LangOpts) {
-    if (InitializedLanguage || M.Kind != serialization::MK_MainFile)
+    if (InitializedLanguage)
       return false;
     
-    LangOpt = LangOpts;
-    
-    // Initialize the preprocessor.
-    PP.Initialize(*Target);
-    
-    // Initialize the ASTContext
-    Context.InitBuiltinTypes(*Target);
-    
-    InitializedLanguage = true;
+    assert(M.Kind == serialization::MK_MainFile);
 
-    applyLangOptsToTarget();
+    LangOpt = LangOpts;
+    InitializedLanguage = true;
+    
+    updated();
     return false;
   }
 
   virtual bool ReadTargetTriple(const serialization::ModuleFile &M,
                                 StringRef Triple) {
     // If we've already initialized the target, don't do it again.
-    if (Target || M.Kind != serialization::MK_MainFile)
+    if (Target)
       return false;
     
+    assert(M.Kind == serialization::MK_MainFile);
+
     // FIXME: This is broken, we should store the TargetOptions in the AST file.
     TargetOptions TargetOpts;
     TargetOpts.ABI = "";
@@ -546,7 +551,7 @@
     TargetOpts.Triple = Triple;
     Target = TargetInfo::CreateTargetInfo(PP.getDiagnostics(), TargetOpts);
 
-    applyLangOptsToTarget();
+    updated();
     return false;
   }
 
@@ -570,14 +575,21 @@
   }
 
 private:
-  void applyLangOptsToTarget() {
-    if (Target && InitializedLanguage) {
-      // 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->setForcedLangOptions(LangOpt);
-    }
+  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->setForcedLangOptions(LangOpt);
+
+    // Initialize the preprocessor.
+    PP.Initialize(*Target);
+
+    // Initialize the ASTContext
+    Context.InitBuiltinTypes(*Target);
   }
 };
 
@@ -642,6 +654,12 @@
   return OriginalSourceFile;
 }
 
+ASTDeserializationListener *ASTUnit::getDeserializationListener() {
+  if (WriterData)
+    return &WriterData->Writer;
+  return 0;
+}
+
 llvm::MemoryBuffer *ASTUnit::getBufferForFile(StringRef Filename,
                                               std::string *ErrorStr) {
   assert(FileMgr);
@@ -917,6 +935,10 @@
     for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it)
       handleTopLevelDecl(*it);
   }
+
+  virtual ASTDeserializationListener *GetASTDeserializationListener() {
+    return Unit.getDeserializationListener();
+  }
 };
 
 class TopLevelDeclTrackerAction : public ASTFrontendAction {
@@ -1929,6 +1951,7 @@
                                       bool AllowPCHWithCompilerErrors,
                                       bool SkipFunctionBodies,
                                       bool UserFilesAreVolatile,
+                                      bool ForSerialization,
                                       OwningPtr<ASTUnit> *ErrAST) {
   if (!Diags.getPtr()) {
     // No diagnostics engine was provided, so create our own diagnostics object
@@ -1992,6 +2015,8 @@
   AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size();
   AST->StoredDiagnostics.swap(StoredDiagnostics);
   AST->Invocation = CI;
+  if (ForSerialization)
+    AST->WriterData.reset(new ASTWriterData());
   CI = 0; // Zero out now to ease cleanup during crash recovery.
   
   // Recover resources if we crash before exiting this method.
@@ -2506,20 +2531,31 @@
   return false;
 }
 
+static bool serializeUnit(ASTWriter &Writer,
+                          SmallVectorImpl<char> &Buffer,
+                          Sema &S,
+                          bool hasErrors,
+                          raw_ostream &OS) {
+  Writer.WriteAST(S, 0, std::string(), 0, "", 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);
-  // FIXME: Handle modules
-  Writer.WriteAST(getSema(), 0, std::string(), 0, "", hasErrors);
-  
-  // Write the generated bitstream to "Out".
-  if (!Buffer.empty())
-    OS.write((char *)&Buffer.front(), Buffer.size());
-
-  return false;
+  return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS);
 }
 
 typedef ContinuousRangeMap<unsigned, int, 2> SLocRemap;