400 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- SerializationTest.cpp - Experimental Object Serialization --------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file was developed by Ted Kremenek and is distributed under
 | 
						|
// the University of Illinois Open Source License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
//  This file implements prototype code for serialization of objects in clang.
 | 
						|
//  It is not intended yet for public use, but simply is a placeholder to
 | 
						|
//  experiment with new serialization features.  Serialization will eventually
 | 
						|
//  be integrated as a proper component of the clang libraries.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "ASTConsumers.h"
 | 
						|
#include "clang/AST/AST.h"
 | 
						|
#include "clang/AST/ASTConsumer.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/AST/CFG.h"
 | 
						|
#include "llvm/System/Path.h"
 | 
						|
#include "llvm/Support/Streams.h"
 | 
						|
#include "llvm/Support/MemoryBuffer.h"
 | 
						|
#include "llvm/Bitcode/Serialize.h"
 | 
						|
#include "llvm/Bitcode/Deserialize.h"
 | 
						|
#include <fstream>
 | 
						|
#include <stdio.h>
 | 
						|
#include <list>
 | 
						|
 | 
						|
using namespace clang;
 | 
						|
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
// Utility classes
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
template<typename T> class Janitor {
 | 
						|
  T* Obj;
 | 
						|
public:
 | 
						|
  explicit Janitor(T* obj) : Obj(obj) {}
 | 
						|
  ~Janitor() { delete Obj; }
 | 
						|
  operator T*() const { return Obj; }
 | 
						|
  T* operator->() { return Obj; }
 | 
						|
};
 | 
						|
  
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
// Driver code.
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
class SerializationTest : public ASTConsumer {
 | 
						|
  ASTContext* Context;
 | 
						|
  std::list<Decl*> Decls;
 | 
						|
  
 | 
						|
  enum { BasicMetadataBlock = 1,
 | 
						|
         ASTContextBlock = 2,
 | 
						|
         DeclsBlock = 3 };
 | 
						|
 | 
						|
public:  
 | 
						|
  SerializationTest() : Context(NULL) {};
 | 
						|
  ~SerializationTest();
 | 
						|
 | 
						|
  virtual void Initialize(ASTContext& context, unsigned) {
 | 
						|
      Context = &context;
 | 
						|
  }
 | 
						|
  
 | 
						|
  virtual void HandleTopLevelDecl(Decl *D) {
 | 
						|
    Decls.push_back(D);
 | 
						|
  }
 | 
						|
 | 
						|
private:
 | 
						|
  void Serialize(llvm::sys::Path& Filename, llvm::sys::Path& FNameDeclPrint);
 | 
						|
  void Deserialize(llvm::sys::Path& Filename, llvm::sys::Path& FNameDeclPrint);
 | 
						|
};
 | 
						|
  
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
ASTConsumer* clang::CreateSerializationTest() {  
 | 
						|
  return new SerializationTest();
 | 
						|
}
 | 
						|
 | 
						|
static void WritePreamble(llvm::BitstreamWriter& Stream) {
 | 
						|
  Stream.Emit((unsigned)'B', 8);
 | 
						|
  Stream.Emit((unsigned)'C', 8);
 | 
						|
  Stream.Emit(0xC, 4);
 | 
						|
  Stream.Emit(0xF, 4);
 | 
						|
  Stream.Emit(0xE, 4);
 | 
						|
  Stream.Emit(0x0, 4);
 | 
						|
}
 | 
						|
 | 
						|
static bool ReadPreamble(llvm::BitstreamReader& Stream) {
 | 
						|
  return Stream.Read(8) != 'B' ||
 | 
						|
         Stream.Read(8) != 'C' ||
 | 
						|
         Stream.Read(4) != 0xC ||
 | 
						|
         Stream.Read(4) != 0xF ||
 | 
						|
         Stream.Read(4) != 0xE ||
 | 
						|
         Stream.Read(4) != 0x0;
 | 
						|
}
 | 
						|
 | 
						|
void SerializationTest::Serialize(llvm::sys::Path& Filename,
 | 
						|
                                  llvm::sys::Path& FNameDeclPrint) {
 | 
						|
  
 | 
						|
  // Reserve 256K for bitstream buffer.
 | 
						|
  std::vector<unsigned char> Buffer;
 | 
						|
  Buffer.reserve(256*1024);
 | 
						|
  
 | 
						|
  // Create bitstream and write preamble.    
 | 
						|
  llvm::BitstreamWriter Stream(Buffer);
 | 
						|
  WritePreamble(Stream);
 | 
						|
  
 | 
						|
  // Create serializer.
 | 
						|
  llvm::Serializer Sezr(Stream);
 | 
						|
  
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
  //      Serialize the top-level decls.
 | 
						|
  // ===---------------------------------------------------===/  
 | 
						|
  
 | 
						|
  Sezr.EnterBlock(DeclsBlock);
 | 
						|
    
 | 
						|
  { // Create a printer to "consume" our deserialized ASTS.
 | 
						|
 | 
						|
    Janitor<ASTConsumer> Printer(CreateASTPrinter());
 | 
						|
    std::ofstream DeclPP(FNameDeclPrint.c_str());
 | 
						|
    assert (DeclPP && "Could not open file for printing out decls.");
 | 
						|
    Janitor<ASTConsumer> FilePrinter(CreateASTPrinter(&DeclPP));
 | 
						|
    
 | 
						|
    for (std::list<Decl*>::iterator I=Decls.begin(), E=Decls.end(); I!=E; ++I) {
 | 
						|
      llvm::cerr << "Serializing: Decl.\n";   
 | 
						|
      
 | 
						|
      // Only serialize the head of a decl chain.  The ASTConsumer interfaces
 | 
						|
      // provides us with each top-level decl, including those nested in
 | 
						|
      // a decl chain, so we may be passed decls that are already serialized.
 | 
						|
      if (!Sezr.isRegistered(*I)) {
 | 
						|
        Printer->HandleTopLevelDecl(*I);
 | 
						|
        FilePrinter->HandleTopLevelDecl(*I);
 | 
						|
        
 | 
						|
        if (FunctionDecl* FD = dyn_cast<FunctionDecl>(*I))
 | 
						|
          if (FD->getBody()) {
 | 
						|
            // Construct and print a CFG.
 | 
						|
            Janitor<CFG> cfg(CFG::buildCFG(FD->getBody()));
 | 
						|
            cfg->print(DeclPP);
 | 
						|
          }
 | 
						|
        
 | 
						|
        // Serialize the decl.
 | 
						|
        Sezr.EmitOwnedPtr(*I);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  Sezr.ExitBlock();
 | 
						|
  
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
  //      Serialize the "Translation Unit" metadata.
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
 | 
						|
  // Emit ASTContext.
 | 
						|
  Sezr.EnterBlock(ASTContextBlock);  
 | 
						|
  llvm::cerr << "Serializing: ASTContext.\n";  
 | 
						|
  Sezr.EmitOwnedPtr(Context);  
 | 
						|
  Sezr.ExitBlock();  
 | 
						|
  
 | 
						|
  
 | 
						|
  Sezr.EnterBlock(BasicMetadataBlock);
 | 
						|
 | 
						|
  // Block for SourceManager and Target.  Allows easy skipping around
 | 
						|
  // to the Selectors during deserialization.
 | 
						|
  Sezr.EnterBlock();
 | 
						|
 | 
						|
  // "Fake" emit the SourceManager.
 | 
						|
  llvm::cerr << "Faux-serializing: SourceManager.\n";
 | 
						|
  Sezr.EmitPtr(&Context->SourceMgr);
 | 
						|
  
 | 
						|
  // "Fake" emit the Target.
 | 
						|
  llvm::cerr << "Faux-serializing: Target.\n";
 | 
						|
  Sezr.EmitPtr(&Context->Target);
 | 
						|
 | 
						|
  Sezr.ExitBlock();
 | 
						|
 | 
						|
  // Emit the Selectors.
 | 
						|
  llvm::cerr << "Serializing: Selectors.\n";
 | 
						|
  Sezr.Emit(Context->Selectors);
 | 
						|
  
 | 
						|
  // Emit the Identifier Table.
 | 
						|
  llvm::cerr << "Serializing: IdentifierTable.\n";  
 | 
						|
  Sezr.Emit(Context->Idents);
 | 
						|
 | 
						|
  Sezr.ExitBlock();  
 | 
						|
  
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
  // Finalize serialization: write the bits to disk.
 | 
						|
  if (FILE* fp = fopen(Filename.c_str(),"wb")) {
 | 
						|
    fwrite((char*)&Buffer.front(), sizeof(char), Buffer.size(), fp);
 | 
						|
    fclose(fp);
 | 
						|
  }
 | 
						|
  else { 
 | 
						|
    llvm::cerr << "Error: Cannot open " << Filename.c_str() << "\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  llvm::cerr << "Commited bitstream to disk: " << Filename.c_str() << "\n";
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void SerializationTest::Deserialize(llvm::sys::Path& Filename,
 | 
						|
                                    llvm::sys::Path& FNameDeclPrint) {
 | 
						|
  
 | 
						|
  // Create the memory buffer that contains the contents of the file.
 | 
						|
  
 | 
						|
  using llvm::MemoryBuffer;
 | 
						|
  
 | 
						|
  Janitor<MemoryBuffer> MBuffer(MemoryBuffer::getFile(Filename.c_str(),
 | 
						|
                                              strlen(Filename.c_str())));
 | 
						|
  
 | 
						|
  if(!MBuffer) {
 | 
						|
    llvm::cerr << "ERROR: Cannot read file for deserialization.\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Check if the file is of the proper length.
 | 
						|
  if (MBuffer->getBufferSize() & 0x3) {
 | 
						|
    llvm::cerr << "ERROR: AST file length should be a multiple of 4 bytes.\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Create the bitstream reader.
 | 
						|
  unsigned char *BufPtr = (unsigned char *) MBuffer->getBufferStart();
 | 
						|
  llvm::BitstreamReader Stream(BufPtr,BufPtr+MBuffer->getBufferSize());
 | 
						|
  
 | 
						|
  // Sniff for the signature in the bitcode file.
 | 
						|
  if (ReadPreamble(Stream)) {
 | 
						|
    llvm::cerr << "ERROR: Invalid AST-bitcode signature.\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
    
 | 
						|
  // Create the deserializer.
 | 
						|
  llvm::Deserializer Dezr(Stream);
 | 
						|
  
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
  //      Deserialize the "Translation Unit" metadata.
 | 
						|
  // ===---------------------------------------------------===/
 | 
						|
  
 | 
						|
  // Skip to the BasicMetaDataBlock.  First jump to ASTContextBlock
 | 
						|
  // (which will appear earlier) and record its location.
 | 
						|
  
 | 
						|
  bool FoundBlock = Dezr.SkipToBlock(ASTContextBlock);
 | 
						|
  assert (FoundBlock);
 | 
						|
 | 
						|
  llvm::Deserializer::Location ASTContextBlockLoc =
 | 
						|
    Dezr.getCurrentBlockLocation();
 | 
						|
  
 | 
						|
  FoundBlock = Dezr.SkipToBlock(BasicMetadataBlock);
 | 
						|
  assert (FoundBlock);
 | 
						|
  
 | 
						|
  // "Fake" read the SourceManager.
 | 
						|
  llvm::cerr << "Faux-Deserializing: SourceManager.\n";
 | 
						|
  Dezr.RegisterPtr(&Context->SourceMgr);
 | 
						|
 | 
						|
  // "Fake" read the TargetInfo.
 | 
						|
  llvm::cerr << "Faux-Deserializing: Target.\n";
 | 
						|
  Dezr.RegisterPtr(&Context->Target);
 | 
						|
 | 
						|
  // For Selectors, we must read the identifier table first because the
 | 
						|
  //  SelectorTable depends on the identifiers being already deserialized.
 | 
						|
  llvm::Deserializer::Location SelectorBlockLoc = Dezr.getCurrentBlockLocation();
 | 
						|
  Dezr.SkipBlock();
 | 
						|
  
 | 
						|
  // Read the identifier table.
 | 
						|
  llvm::cerr << "Deserializing: IdentifierTable\n";
 | 
						|
  IdentifierTable::CreateAndRegister(Dezr);
 | 
						|
  
 | 
						|
  // Now jump back and read the selectors.
 | 
						|
  llvm::cerr << "Deserializing: Selectors\n";
 | 
						|
  Dezr.JumpTo(SelectorBlockLoc);
 | 
						|
  SelectorTable::CreateAndRegister(Dezr);
 | 
						|
  
 | 
						|
  // Now jump back to ASTContextBlock and read the ASTContext.
 | 
						|
  llvm::cerr << "Deserializing: ASTContext.\n";
 | 
						|
  Dezr.JumpTo(ASTContextBlockLoc);
 | 
						|
  Dezr.ReadOwnedPtr<ASTContext>();
 | 
						|
    
 | 
						|
  // "Rewind" the stream.  Find the block with the serialized top-level decls.
 | 
						|
  Dezr.Rewind();
 | 
						|
  FoundBlock = Dezr.SkipToBlock(DeclsBlock);
 | 
						|
  assert (FoundBlock);
 | 
						|
  llvm::Deserializer::Location DeclBlockLoc = Dezr.getCurrentBlockLocation();
 | 
						|
  
 | 
						|
  // Create a printer to "consume" our deserialized ASTS.
 | 
						|
  ASTConsumer* Printer = CreateASTPrinter();
 | 
						|
  Janitor<ASTConsumer> PrinterJanitor(Printer);  
 | 
						|
  std::ofstream DeclPP(FNameDeclPrint.c_str());
 | 
						|
  assert (DeclPP && "Could not open file for printing out decls.");
 | 
						|
  Janitor<ASTConsumer> FilePrinter(CreateASTPrinter(&DeclPP));
 | 
						|
  
 | 
						|
  // The remaining objects in the file are top-level decls.
 | 
						|
  while (!Dezr.FinishedBlock(DeclBlockLoc)) {
 | 
						|
    llvm::cerr << "Deserializing: Decl.\n";
 | 
						|
    Decl* decl = Dezr.ReadOwnedPtr<Decl>();
 | 
						|
    Printer->HandleTopLevelDecl(decl);
 | 
						|
    FilePrinter->HandleTopLevelDecl(decl);
 | 
						|
    
 | 
						|
    if (FunctionDecl* FD = dyn_cast<FunctionDecl>(decl))
 | 
						|
      if (FD->getBody()) {
 | 
						|
        // Construct and print a CFG.
 | 
						|
        Janitor<CFG> cfg(CFG::buildCFG(FD->getBody()));
 | 
						|
        cfg->print(DeclPP);
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 | 
						|
  
 | 
						|
namespace {
 | 
						|
  class TmpDirJanitor {
 | 
						|
    llvm::sys::Path& Dir;
 | 
						|
  public:
 | 
						|
    explicit TmpDirJanitor(llvm::sys::Path& dir) : Dir(dir) {}
 | 
						|
 | 
						|
    ~TmpDirJanitor() { 
 | 
						|
      llvm::cerr << "Removing: " << Dir.c_str() << '\n';
 | 
						|
      Dir.eraseFromDisk(true); 
 | 
						|
    }
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
SerializationTest::~SerializationTest() {
 | 
						|
 | 
						|
  std::string ErrMsg;
 | 
						|
  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
 | 
						|
  
 | 
						|
  if (Dir.isEmpty()) {
 | 
						|
    llvm::cerr << "Error: " << ErrMsg << "\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  TmpDirJanitor RemoveTmpOnExit(Dir);
 | 
						|
    
 | 
						|
  llvm::sys::Path FNameDeclBefore = Dir;
 | 
						|
  FNameDeclBefore.appendComponent("test.decl_before.txt");
 | 
						|
 | 
						|
  if (FNameDeclBefore.makeUnique(true,&ErrMsg)) {
 | 
						|
    llvm::cerr << "Error: " << ErrMsg << "\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  llvm::sys::Path FNameDeclAfter = Dir;
 | 
						|
  FNameDeclAfter.appendComponent("test.decl_after.txt");
 | 
						|
  
 | 
						|
  if (FNameDeclAfter.makeUnique(true,&ErrMsg)) {
 | 
						|
    llvm::cerr << "Error: " << ErrMsg << "\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  llvm::sys::Path ASTFilename = Dir;
 | 
						|
  ASTFilename.appendComponent("test.ast");
 | 
						|
  
 | 
						|
  if (ASTFilename.makeUnique(true,&ErrMsg)) {
 | 
						|
    llvm::cerr << "Error: " << ErrMsg << "\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  // Serialize and then deserialize the ASTs.
 | 
						|
  Serialize(ASTFilename, FNameDeclBefore);
 | 
						|
  Deserialize(ASTFilename, FNameDeclAfter);
 | 
						|
  
 | 
						|
  // Read both pretty-printed files and compare them.
 | 
						|
  
 | 
						|
  using llvm::MemoryBuffer;
 | 
						|
  
 | 
						|
  Janitor<MemoryBuffer>
 | 
						|
    MBufferSer(MemoryBuffer::getFile(FNameDeclBefore.c_str(),
 | 
						|
                                     strlen(FNameDeclBefore.c_str())));
 | 
						|
  
 | 
						|
  if(!MBufferSer) {
 | 
						|
    llvm::cerr << "ERROR: Cannot read pretty-printed file (pre-pickle).\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  Janitor<MemoryBuffer>
 | 
						|
    MBufferDSer(MemoryBuffer::getFile(FNameDeclAfter.c_str(),
 | 
						|
                                      strlen(FNameDeclAfter.c_str())));
 | 
						|
  
 | 
						|
  if(!MBufferDSer) {
 | 
						|
    llvm::cerr << "ERROR: Cannot read pretty-printed file (post-pickle).\n";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  const char *p1 = MBufferSer->getBufferStart();
 | 
						|
  const char *e1 = MBufferSer->getBufferEnd();
 | 
						|
  const char *p2 = MBufferDSer->getBufferStart();
 | 
						|
  const char *e2 = MBufferDSer->getBufferEnd();
 | 
						|
 | 
						|
  if (MBufferSer->getBufferSize() == MBufferDSer->getBufferSize())
 | 
						|
    for ( ; p1 != e1 ; ++p1, ++p2  )
 | 
						|
      if (*p1 != *p2) break;
 | 
						|
  
 | 
						|
  if (p1 != e1 || p2 != e2 )
 | 
						|
    llvm::cerr << "ERROR: Pretty-printed files are not the same.\n";
 | 
						|
  else
 | 
						|
    llvm::cerr << "SUCCESS: Pretty-printed files are the same.\n";
 | 
						|
}
 |