blob: bd2ec1f571dbaeed62276957561d04ca6d7fceae [file] [log] [blame]
//===--- IndexRecordReader.cpp - Index record deserialization -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/Index/IndexRecordReader.h"
#include "IndexDataStoreUtils.h"
#include "BitstreamVisitor.h"
#include "clang/Index/IndexDataStoreSymbolUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Bitcode/BitstreamReader.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace clang::index;
using namespace clang::index::store;
using namespace llvm;
struct IndexRecordReader::Implementation {
BumpPtrAllocator Allocator;
std::unique_ptr<MemoryBuffer> Buffer;
llvm::BitstreamCursor DeclCursor;
llvm::BitstreamCursor OccurCursor;
ArrayRef<uint32_t> DeclOffsets;
const IndexRecordDecl **Decls;
void setDeclOffsets(ArrayRef<uint32_t> Offs) {
DeclOffsets = Offs;
Decls = Allocator.Allocate<const IndexRecordDecl*>(Offs.size());
memset(Decls, 0, sizeof(IndexRecordDecl*)*Offs.size());
}
unsigned getNumDecls() const { return DeclOffsets.size(); }
const IndexRecordDecl *getDeclByID(unsigned DeclID) {
if (DeclID == 0)
return nullptr;
return getDecl(DeclID-1);
}
const IndexRecordDecl *getDecl(unsigned Index) {
assert(Index < getNumDecls());
if (const IndexRecordDecl *D = Decls[Index])
return D;
IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>();
readDecl(Index, *D);
Decls[Index] = D;
return D;
}
/// Goes through the decls and populates a vector of record decls, based on
/// what the given function returns.
///
/// The advantage of this function is to allocate memory only for the record
/// decls that the caller is interested in.
bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker,
llvm::function_ref<void(const IndexRecordDecl *)> Receiver) {
for (unsigned I = 0, E = getNumDecls(); I != E; ++I) {
if (const IndexRecordDecl *D = Decls[I]) {
DeclSearchReturn Ret = Checker(*D);
if (Ret.AcceptDecl)
Receiver(D);
if (!Ret.ContinueSearch)
return false;
continue;
}
IndexRecordDecl LocalD;
readDecl(I, LocalD);
DeclSearchReturn Ret = Checker(LocalD);
if (Ret.AcceptDecl) {
IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>();
*D = LocalD;
Decls[I] = D;
Receiver(D);
}
if (!Ret.ContinueSearch)
return false;
}
return true;
}
void readDecl(unsigned Index, IndexRecordDecl &RecD) {
RecordData Record;
StringRef Blob;
DeclCursor.JumpToBit(DeclOffsets[Index]);
unsigned Code = DeclCursor.ReadCode();
unsigned RecID = DeclCursor.readRecord(Code, Record, &Blob);
assert(RecID == REC_DECLINFO);
(void)RecID;
unsigned I = 0;
RecD.DeclID = Index+1;
RecD.SymInfo.Kind = getSymbolKind((indexstore_symbol_kind_t)read(Record, I));
RecD.SymInfo.SubKind = getSymbolSubKind((indexstore_symbol_subkind_t)read(Record, I));
RecD.SymInfo.Lang =
getSymbolLanguage((indexstore_symbol_language_t)read(Record, I));
RecD.SymInfo.Properties = getSymbolProperties(read(Record, I));
RecD.Roles = getSymbolRoles(read(Record, I));
RecD.RelatedRoles = getSymbolRoles(read(Record, I));
size_t NameLen = read(Record, I);
size_t USRLen = read(Record, I);
RecD.Name = Blob.substr(0, NameLen);
RecD.USR = Blob.substr(NameLen, USRLen);
RecD.CodeGenName = Blob.substr(NameLen+USRLen);
}
/// Reads occurrence data.
/// \param DeclsFilter if non-empty indicates the list of decls that we want
/// to get occurrences for. If empty then indicates that we want occurrences
/// for all decls.
/// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls.
/// \returns true if the occurrence info was filled out, false if occurrence
/// was ignored.
bool readOccurrence(RecordDataImpl &Record, StringRef Blob,
ArrayRef<const IndexRecordDecl *> DeclsFilter,
ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
IndexRecordOccurrence &RecOccur) {
auto isDeclIDContained = [](unsigned DeclID,
ArrayRef<const IndexRecordDecl *> Ds) -> bool {
if (Ds.empty())
return true; // empty means accept all.
auto pred = [DeclID](const IndexRecordDecl *D) { return D->DeclID == DeclID; };
return std::find_if(Ds.begin(), Ds.end(), pred) != Ds.end();
};
unsigned I = 0;
unsigned DeclID = read(Record, I);
if (!isDeclIDContained(DeclID, DeclsFilter))
return false;
if (!RelatedDeclsFilter.empty()) {
unsigned RelI = I+3;
unsigned NumRelated = read(Record, RelI);
bool FoundRelated = false;
while (NumRelated--) {
++RelI; // roles;
unsigned RelDID = read(Record, RelI);
if (isDeclIDContained(RelDID, RelatedDeclsFilter)) {
FoundRelated = true;
break;
}
}
if (!FoundRelated)
return false;
}
RecOccur.Dcl = getDeclByID(DeclID);
RecOccur.Roles = getSymbolRoles(read(Record, I));
RecOccur.Line = read(Record, I);
RecOccur.Column = read(Record, I);
unsigned NumRelated = read(Record, I);
while (NumRelated--) {
SymbolRoleSet RelRoles = getSymbolRoles(read(Record, I));
const IndexRecordDecl *RelD = getDeclByID(read(Record, I));
RecOccur.Relations.emplace_back(RelRoles, RelD);
}
return true;
}
bool foreachDecl(bool NoCache,
function_ref<bool(const IndexRecordDecl *)> Receiver) {
for (unsigned I = 0, E = getNumDecls(); I != E; ++I) {
if (const IndexRecordDecl *D = Decls[I]) {
if (!Receiver(D))
return false;
continue;
}
if (NoCache) {
IndexRecordDecl LocalD;
readDecl(I, LocalD);
if (!Receiver(&LocalD))
return false;
} else {
if (!Receiver(getDecl(I)))
return false;
}
}
return true;
}
bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter,
ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
class OccurBitVisitor : public BitstreamVisitor<OccurBitVisitor> {
IndexRecordReader::Implementation &Reader;
ArrayRef<const IndexRecordDecl *> DeclsFilter;
ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter;
function_ref<bool(const IndexRecordOccurrence &)> Receiver;
public:
OccurBitVisitor(llvm::BitstreamCursor &Stream,
IndexRecordReader::Implementation &Reader,
ArrayRef<const IndexRecordDecl *> DeclsFilter,
ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
function_ref<bool(const IndexRecordOccurrence &)> Receiver)
: BitstreamVisitor(Stream),
Reader(Reader),
DeclsFilter(DeclsFilter),
RelatedDeclsFilter(RelatedDeclsFilter),
Receiver(std::move(Receiver)) {}
StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
RecordDataImpl &Record, StringRef Blob) {
assert(RecID == REC_DECLOCCURRENCE);
IndexRecordOccurrence RecOccur;
if (Reader.readOccurrence(Record, Blob, DeclsFilter, RelatedDeclsFilter,
RecOccur))
if (!Receiver(RecOccur))
return StreamVisit::Abort;
return StreamVisit::Continue;
}
};
SavedStreamPosition SavedPosition(OccurCursor);
OccurBitVisitor Visitor(OccurCursor, *this, DeclsFilter, RelatedDeclsFilter,
Receiver);
std::string Error;
return Visitor.visit(Error);
}
bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount,
llvm::function_ref<bool(const IndexRecordOccurrence &)> receiver) {
// FIXME: Use binary search and make this more efficient.
unsigned lineEnd = lineStart+lineCount;
return foreachOccurrence(None, None, [&](const IndexRecordOccurrence &occur) -> bool {
if (occur.Line > lineEnd)
return false; // we're done.
if (occur.Line >= lineStart) {
if (!receiver(occur))
return false;
}
return true;
});
}
static uint64_t read(RecordDataImpl &Record, unsigned &I) {
return Record[I++];
}
};
namespace {
class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> {
IndexRecordReader::Implementation &Reader;
public:
IndexBitstreamVisitor(llvm::BitstreamCursor &Stream,
IndexRecordReader::Implementation &Reader)
: BitstreamVisitor(Stream), Reader(Reader) {}
StreamVisit visitBlock(unsigned ID) {
switch ((RecordBitBlock)ID) {
case REC_VERSION_BLOCK_ID:
case REC_DECLOFFSETS_BLOCK_ID:
return StreamVisit::Continue;
case REC_DECLS_BLOCK_ID:
Reader.DeclCursor = Stream;
if (Reader.DeclCursor.EnterSubBlock(ID)) {
*Error = "malformed block record";
return StreamVisit::Abort;
}
readBlockAbbrevs(Reader.DeclCursor);
return StreamVisit::Skip;
case REC_DECLOCCURRENCES_BLOCK_ID:
Reader.OccurCursor = Stream;
if (Reader.OccurCursor.EnterSubBlock(ID)) {
*Error = "malformed block record";
return StreamVisit::Abort;
}
readBlockAbbrevs(Reader.OccurCursor);
return StreamVisit::Skip;
}
// Some newly introduced block in a minor version update that we cannot
// handle.
return StreamVisit::Skip;
}
StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
RecordDataImpl &Record, StringRef Blob) {
switch (BlockID) {
case REC_VERSION_BLOCK_ID: {
unsigned StoreFormatVersion = Record[0];
if (StoreFormatVersion != STORE_FORMAT_VERSION) {
llvm::raw_string_ostream OS(*Error);
OS << "Store format version mismatch: " << StoreFormatVersion;
OS << " , expected: " << STORE_FORMAT_VERSION;
return StreamVisit::Abort;
}
break;
}
case REC_DECLOFFSETS_BLOCK_ID:
assert(RecID == REC_DECLOFFSETS);
Reader.setDeclOffsets(makeArrayRef((uint32_t*)Blob.data(), Record[0]));
break;
case REC_DECLS_BLOCK_ID:
case REC_DECLOCCURRENCES_BLOCK_ID:
llvm_unreachable("shouldn't visit this block'");
}
return StreamVisit::Continue;
}
};
} // anonymous namespace
std::unique_ptr<IndexRecordReader>
IndexRecordReader::createWithRecordFilename(StringRef RecordFilename,
StringRef StorePath,
std::string &Error) {
SmallString<128> PathBuf = StorePath;
appendRecordSubDir(PathBuf);
appendInteriorRecordPath(RecordFilename, PathBuf);
return createWithFilePath(PathBuf.str(), Error);
}
std::unique_ptr<IndexRecordReader>
IndexRecordReader::createWithFilePath(StringRef FilePath, std::string &Error) {
auto ErrOrBuf = MemoryBuffer::getFile(FilePath, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false);
if (!ErrOrBuf) {
raw_string_ostream(Error) << "failed opening index record '"
<< FilePath << "': " << ErrOrBuf.getError().message();
return nullptr;
}
return createWithBuffer(std::move(*ErrOrBuf), Error);
}
std::unique_ptr<IndexRecordReader>
IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::string &Error) {
std::unique_ptr<IndexRecordReader> Reader;
Reader.reset(new IndexRecordReader());
auto &Impl = Reader->Impl;
Impl.Buffer = std::move(Buffer);
llvm::BitstreamCursor Stream(*Impl.Buffer);
// Sniff for the signature.
if (Stream.Read(8) != 'I' ||
Stream.Read(8) != 'D' ||
Stream.Read(8) != 'X' ||
Stream.Read(8) != 'R') {
Error = "not a serialized index record file";
return nullptr;
}
IndexBitstreamVisitor BitVisitor(Stream, Impl);
if (!BitVisitor.visit(Error))
return nullptr;
return Reader;
}
IndexRecordReader::IndexRecordReader()
: Impl(*new Implementation()) {
}
IndexRecordReader::~IndexRecordReader() {
delete &Impl;
}
bool IndexRecordReader::searchDecls(
llvm::function_ref<DeclSearchCheck> Checker,
llvm::function_ref<void(const IndexRecordDecl *)> Receiver) {
return Impl.searchDecls(std::move(Checker), std::move(Receiver));
}
bool IndexRecordReader::foreachDecl(bool NoCache,
function_ref<bool(const IndexRecordDecl *)> Receiver) {
return Impl.foreachDecl(NoCache, std::move(Receiver));
}
bool IndexRecordReader::foreachOccurrence(
ArrayRef<const IndexRecordDecl *> DeclsFilter,
ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
return Impl.foreachOccurrence(DeclsFilter, RelatedDeclsFilter,
std::move(Receiver));
}
bool IndexRecordReader::foreachOccurrence(
llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
return foreachOccurrence(None, None, std::move(Receiver));
}
bool IndexRecordReader::foreachOccurrenceInLineRange(unsigned lineStart,
unsigned lineCount,
llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
return Impl.foreachOccurrenceInLineRange(lineStart, lineCount, Receiver);
}