//===--- 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 "llvm/System/Path.h"
#include "llvm/Bitcode/BitstreamWriter.h"
#include <fstream>
#include <iostream>

using namespace clang;
using llvm::BitstreamWriter;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;

namespace llvm {  
template<typename T> struct IntrospectionTrait {
  struct Flags { 
    enum { isPod = false, UniqueInstances = false, UniqueRefs = false };
  };
  
  template<typename Introspector>
  struct Ops {
    static inline void Introspect(T& X, Introspector& I) {
      assert (false && "Introspect not implemented.");
    }
  };
};
}

namespace {
class SerializationTest : public ASTConsumer {
  IdentifierTable* IdTable;
  unsigned MainFileID;
public:
  void Initialize(ASTContext& Context, unsigned mainFileID) {
    IdTable = &Context.Idents;
    MainFileID = mainFileID;
    RunSerializationTest();
  }
  
  void RunSerializationTest();
  bool WriteAll(llvm::sys::Path& Filename);
  
  virtual void HandleTopLevelDecl(Decl *D) {}
};

class Writer {
  std::vector<unsigned char> Buffer;
  BitstreamWriter Stream;
  std::ostream& Out;
public:
  
  enum { IdentifierTableBID = 0x8 };
  
  Writer(std::ostream& out) : Stream(Buffer), Out(out) {
    Buffer.reserve(256*1024);
    
    // Emit the file header.
    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);
  }
  
  ~Writer() {
    Out.write((char*)&Buffer.front(), Buffer.size());
    Out.flush();
  }
  
  template <typename T> inline void operator()(T& x) {
    llvm::IntrospectionTrait<T>::template Ops<Writer>::Introspect(x,*this);
  }
    
  template <typename T> inline void operator()(T& x, unsigned bits) {
    llvm::IntrospectionTrait<T>::template Ops<Writer>::Introspect(x,bits,*this);
  }
  
  template <typename T> inline void operator()(const T& x) {
    operator()(const_cast<T&>(x));
  }
  
  template <typename T> inline void operator()(const T& x, unsigned bits) {
    operator()(const_cast<T&>(x),bits);
  }  
  
  inline void operator()(bool X) { Stream.Emit(X,1); }
  inline void operator()(unsigned X) { Stream.Emit(X,32); }
  inline void operator()(unsigned X, unsigned bits, bool VBR=false) {
    if (VBR) Stream.Emit(X,bits);
    else Stream.Emit(X,bits);
  }
  
  inline BitstreamWriter& getStream() {
    return Stream;
  }
  
  template <typename T> inline void EnterSubblock(unsigned CodeLen) {
    Stream.EnterSubblock(8,CodeLen);
  }
  
  inline void ExitBlock() { Stream.ExitBlock(); }
  
};  
  
} // end anonymous namespace  

//===----------------------------------------------------------------------===//
// External Interface.
//===----------------------------------------------------------------------===//

ASTConsumer* clang::CreateSerializationTest() {
  return new SerializationTest();
}
  
//===----------------------------------------------------------------------===//
// Serialization "Driver" code.
//===----------------------------------------------------------------------===//

void SerializationTest::RunSerializationTest() { 
  std::string ErrMsg;
  llvm::sys::Path Filename = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);

  if (Filename.isEmpty()) {
    cerr << "Error: " << ErrMsg << "\n";
    return;
  }
  
  Filename.appendComponent("test.cfe_bc");
  
  if (Filename.makeUnique(true,&ErrMsg)) {
    cerr << "Error: " << ErrMsg << "\n";
    return;
  }
  
  if (!WriteAll(Filename))
    return;
  
  cout << "Wrote file: " << Filename.c_str() << "\n";
}

bool SerializationTest::WriteAll(llvm::sys::Path& Filename) {  
  std::ofstream Out(Filename.c_str());
  
  if (!Out) {
    cerr << "Error: Cannot open " << Filename.c_str() << "\n";
    return false;
  }
    
  Writer W(Out);
  W(*IdTable);

  W.getStream().FlushToWord();
  return true;
}

//===----------------------------------------------------------------------===//
// Serialization Methods.
//===----------------------------------------------------------------------===//

namespace llvm {

struct IntrospectionPrimitivesFlags {
  enum { isPod = true, UniqueInstances = false, UniqueRefs = false };
};
  

template<> struct
IntrospectionTrait<bool>::Flags : public IntrospectionPrimitivesFlags {};
  
template<> struct
IntrospectionTrait<unsigned>::Flags : public IntrospectionPrimitivesFlags {};

template<> struct
IntrospectionTrait<short>::Flags : public IntrospectionPrimitivesFlags {};



template<> 
struct IntrospectionTrait<clang::IdentifierInfo> {

  struct Flags { 
    enum { isPod = false,  // Cannot copy via memcpy.  Must use copy-ctor.    
           hasUniqueInstances = true, // Two pointers with different
                                      // addreses point to objects
                                      // that are not equal to each other.    
           hasUniqueReferences = true // Two (non-temporary) pointers                                    
                                      // will point to distinct instances.
    };
  };

  template<typename Introspector>
  struct Ops {
    static void Introspect(clang::IdentifierInfo& X, Introspector& I) {
  //    I(X.getTokenID());
      I(X.getBuiltinID(),9); // FIXME: do 9 bit specialization.
  //    I(X.getObjCKeywordID());
      I(X.hasMacroDefinition());
      I(X.isExtensionToken());
      I(X.isPoisoned());
      I(X.isOtherTargetMacro());
      I(X.isCPlusPlusOperatorKeyword());
      I(X.isNonPortableBuiltin());
    }
  };
};
  
template<> template<>
struct IntrospectionTrait<clang::IdentifierTable>::Ops<Writer> {
  static void Introspect(clang::IdentifierTable& X, Writer& W) {
    W.EnterSubblock<clang::IdentifierTable>(1);
/*        
    for (clang::IdentifierTable::iterator I = X.begin(), E = X.end();
         I != E; ++I)
      W(I->getValue());
   */ 
    W.ExitBlock();
  }
};

  

  
} // end namespace llvm

