blob: fa51d01355ca5c8edfe167e6d0dbeca1cfc9fae6 [file] [log] [blame]
//===-- BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "BitcodeReader.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace doc {
using Record = llvm::SmallVector<uint64_t, 1024>;
bool decodeRecord(Record R, llvm::SmallVectorImpl<char> &Field,
llvm::StringRef Blob) {
Field.assign(Blob.begin(), Blob.end());
return true;
}
bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) {
if (R[0] != BitCodeConstants::USRHashSize)
return false;
// First position in the record is the length of the following array, so we
// copy the following elements to the field.
for (int I = 0, E = R[0]; I < E; ++I)
Field[I] = R[I + 1];
return true;
}
bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) {
Field = R[0] != 0;
return true;
}
bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field = (int)R[0];
return true;
}
bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) {
switch (R[0]) {
case AS_public:
case AS_private:
case AS_protected:
case AS_none:
Field = (AccessSpecifier)R[0];
return true;
default:
return false;
}
}
bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) {
switch (R[0]) {
case TTK_Struct:
case TTK_Interface:
case TTK_Union:
case TTK_Class:
case TTK_Enum:
Field = (TagTypeKind)R[0];
return true;
default:
return false;
}
}
bool decodeRecord(Record R, llvm::Optional<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field.emplace((int)R[0], Blob);
return true;
}
bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) {
switch (auto IT = static_cast<InfoType>(R[0])) {
case InfoType::IT_namespace:
case InfoType::IT_record:
case InfoType::IT_function:
case InfoType::IT_default:
case InfoType::IT_enum:
Field = IT;
return true;
}
return false;
}
bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) {
switch (auto F = static_cast<FieldId>(R[0])) {
case FieldId::F_namespace:
case FieldId::F_parent:
case FieldId::F_vparent:
case FieldId::F_type:
case FieldId::F_default:
Field = F;
return true;
}
return false;
}
bool decodeRecord(Record R, llvm::SmallVectorImpl<llvm::SmallString<16>> &Field,
llvm::StringRef Blob) {
Field.push_back(Blob);
return true;
}
bool decodeRecord(Record R, llvm::SmallVectorImpl<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field.emplace_back((int)R[0], Blob);
return true;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
const unsigned VersionNo) {
if (ID == VERSION && R[0] == VersionNo)
return true;
return false;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
NamespaceInfo *I) {
switch (ID) {
case NAMESPACE_USR:
return decodeRecord(R, I->USR, Blob);
case NAMESPACE_NAME:
return decodeRecord(R, I->Name, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) {
switch (ID) {
case RECORD_USR:
return decodeRecord(R, I->USR, Blob);
case RECORD_NAME:
return decodeRecord(R, I->Name, Blob);
case RECORD_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case RECORD_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case RECORD_TAG_TYPE:
return decodeRecord(R, I->TagType, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) {
switch (ID) {
case ENUM_USR:
return decodeRecord(R, I->USR, Blob);
case ENUM_NAME:
return decodeRecord(R, I->Name, Blob);
case ENUM_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case ENUM_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case ENUM_MEMBER:
return decodeRecord(R, I->Members, Blob);
case ENUM_SCOPED:
return decodeRecord(R, I->Scoped, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) {
switch (ID) {
case FUNCTION_USR:
return decodeRecord(R, I->USR, Blob);
case FUNCTION_NAME:
return decodeRecord(R, I->Name, Blob);
case FUNCTION_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case FUNCTION_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case FUNCTION_ACCESS:
return decodeRecord(R, I->Access, Blob);
case FUNCTION_IS_METHOD:
return decodeRecord(R, I->IsMethod, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) {
return true;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
FieldTypeInfo *I) {
switch (ID) {
case FIELD_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
MemberTypeInfo *I) {
switch (ID) {
case MEMBER_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
case MEMBER_TYPE_ACCESS:
return decodeRecord(R, I->Access, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) {
switch (ID) {
case COMMENT_KIND:
return decodeRecord(R, I->Kind, Blob);
case COMMENT_TEXT:
return decodeRecord(R, I->Text, Blob);
case COMMENT_NAME:
return decodeRecord(R, I->Name, Blob);
case COMMENT_DIRECTION:
return decodeRecord(R, I->Direction, Blob);
case COMMENT_PARAMNAME:
return decodeRecord(R, I->ParamName, Blob);
case COMMENT_CLOSENAME:
return decodeRecord(R, I->CloseName, Blob);
case COMMENT_ATTRKEY:
return decodeRecord(R, I->AttrKeys, Blob);
case COMMENT_ATTRVAL:
return decodeRecord(R, I->AttrValues, Blob);
case COMMENT_ARG:
return decodeRecord(R, I->Args, Blob);
case COMMENT_SELFCLOSING:
return decodeRecord(R, I->SelfClosing, Blob);
case COMMENT_EXPLICIT:
return decodeRecord(R, I->Explicit, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I,
FieldId &F) {
switch (ID) {
case REFERENCE_USR:
return decodeRecord(R, I->USR, Blob);
case REFERENCE_NAME:
return decodeRecord(R, I->Name, Blob);
case REFERENCE_TYPE:
return decodeRecord(R, I->RefType, Blob);
case REFERENCE_FIELD:
return decodeRecord(R, F, Blob);
default:
return false;
}
}
template <typename T> CommentInfo *getCommentInfo(T I) {
llvm::errs() << "Cannot have comment subblock.\n";
exit(1);
}
template <> CommentInfo *getCommentInfo(FunctionInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(NamespaceInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(RecordInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(EnumInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(llvm::make_unique<CommentInfo>());
return I->Children.back().get();
}
template <> CommentInfo *getCommentInfo(std::unique_ptr<CommentInfo> &I) {
return getCommentInfo(I.get());
}
template <typename T, typename TTypeInfo>
void addTypeInfo(T I, TTypeInfo &&TI) {
llvm::errs() << "Invalid type for info.\n";
exit(1);
}
template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) {
I->Members.emplace_back(std::move(T));
}
template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) {
I->ReturnType = std::move(T);
}
template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) {
I->Params.emplace_back(std::move(T));
}
template <typename T> void addReference(T I, Reference &&R, FieldId F) {
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
case FieldId::F_parent:
I->Parent = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
case FieldId::F_parent:
I->Parents.emplace_back(std::move(R));
break;
case FieldId::F_vparent:
I->VirtualParents.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
// Read records from bitcode into a given info.
template <typename T> bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
Record R;
llvm::StringRef Blob;
unsigned RecID = Stream.readRecord(ID, R, &Blob);
return parseRecord(R, RecID, Blob, I);
}
template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) {
Record R;
llvm::StringRef Blob;
unsigned RecID = Stream.readRecord(ID, R, &Blob);
return parseRecord(R, RecID, Blob, I, CurrentReferenceField);
}
// Read a block of records into a single info.
template <typename T> bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) {
if (Stream.EnterSubBlock(ID))
return false;
while (true) {
unsigned BlockOrCode = 0;
Cursor Res = skipUntilRecordOrBlock(BlockOrCode);
switch (Res) {
case Cursor::BadBlock:
return false;
case Cursor::BlockEnd:
return true;
case Cursor::BlockBegin:
if (readSubBlock(BlockOrCode, I))
continue;
if (!Stream.SkipBlock())
return false;
continue;
case Cursor::Record:
break;
}
if (!readRecord(BlockOrCode, I))
return false;
}
}
template <typename T>
bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
switch (ID) {
// Blocks can only have Comment, Reference, or TypeInfo subblocks
case BI_COMMENT_BLOCK_ID:
if (readBlock(ID, getCommentInfo(I)))
return true;
return false;
case BI_TYPE_BLOCK_ID: {
TypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_FIELD_TYPE_BLOCK_ID: {
FieldTypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_MEMBER_TYPE_BLOCK_ID: {
MemberTypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_REFERENCE_BLOCK_ID: {
Reference R;
if (readBlock(ID, &R)) {
addReference(I, std::move(R), CurrentReferenceField);
return true;
}
return false;
}
default:
llvm::errs() << "Invalid subblock type.\n";
return false;
}
}
ClangDocBitcodeReader::Cursor
ClangDocBitcodeReader::skipUntilRecordOrBlock(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 Cursor::BadBlock;
return Cursor::BlockEnd;
case llvm::bitc::DEFINE_ABBREV:
Stream.ReadAbbrevRecord();
continue;
case llvm::bitc::UNABBREV_RECORD:
return Cursor::BadBlock;
default:
BlockOrRecordID = Code;
return Cursor::Record;
}
}
llvm_unreachable("Premature stream end.");
}
bool ClangDocBitcodeReader::validateStream() {
if (Stream.AtEndOfStream())
return false;
// Sniff for the signature.
if (Stream.Read(8) != BitCodeConstants::Signature[0] ||
Stream.Read(8) != BitCodeConstants::Signature[1] ||
Stream.Read(8) != BitCodeConstants::Signature[2] ||
Stream.Read(8) != BitCodeConstants::Signature[3])
return false;
return true;
}
bool ClangDocBitcodeReader::readBlockInfoBlock() {
BlockInfo = Stream.ReadBlockInfoBlock();
if (!BlockInfo)
return false;
Stream.setBlockInfo(&*BlockInfo);
return true;
}
template <typename T>
std::unique_ptr<Info> ClangDocBitcodeReader::createInfo(unsigned ID) {
std::unique_ptr<Info> I = llvm::make_unique<T>();
if (readBlock(ID, static_cast<T *>(I.get())))
return I;
llvm::errs() << "Error reading from block.\n";
return nullptr;
}
std::unique_ptr<Info> ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
switch (ID) {
case BI_NAMESPACE_BLOCK_ID:
return createInfo<NamespaceInfo>(ID);
case BI_RECORD_BLOCK_ID:
return createInfo<RecordInfo>(ID);
case BI_ENUM_BLOCK_ID:
return createInfo<EnumInfo>(ID);
case BI_FUNCTION_BLOCK_ID:
return createInfo<FunctionInfo>(ID);
default:
llvm::errs() << "Error reading from block.\n";
return nullptr;
}
}
// Entry point
std::vector<std::unique_ptr<Info>> ClangDocBitcodeReader::readBitcode() {
std::vector<std::unique_ptr<Info>> Infos;
if (!validateStream())
return Infos;
// Read the top level blocks.
while (!Stream.AtEndOfStream()) {
unsigned Code = Stream.ReadCode();
if (Code != llvm::bitc::ENTER_SUBBLOCK)
return Infos;
unsigned ID = Stream.ReadSubBlockID();
switch (ID) {
// NamedType and Comment blocks should not appear at the top level
case BI_TYPE_BLOCK_ID:
case BI_FIELD_TYPE_BLOCK_ID:
case BI_MEMBER_TYPE_BLOCK_ID:
case BI_COMMENT_BLOCK_ID:
case BI_REFERENCE_BLOCK_ID:
llvm::errs() << "Invalid top level block.\n";
return Infos;
case BI_NAMESPACE_BLOCK_ID:
case BI_RECORD_BLOCK_ID:
case BI_ENUM_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID:
if (std::unique_ptr<Info> I = readBlockToInfo(ID)) {
Infos.emplace_back(std::move(I));
}
return Infos;
case BI_VERSION_BLOCK_ID:
if (readBlock(ID, VersionNumber))
continue;
return Infos;
case llvm::bitc::BLOCKINFO_BLOCK_ID:
if (readBlockInfoBlock())
continue;
return Infos;
default:
if (!Stream.SkipBlock())
continue;
}
}
return Infos;
}
} // namespace doc
} // namespace clang