| //===-- BitcodeWriter.cpp - ClangDoc Bitcode Writer ------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "BitcodeWriter.h" | |
| #include "llvm/ADT/IndexedMap.h" | |
| #include <initializer_list> | |
| using namespace clang; | |
| using namespace clang::doc; | |
| using namespace llvm; | |
| namespace { | |
| // Empty SymbolID for comparison, so we don't have to construct one every time. | |
| static const SymbolID EmptySID = SymbolID(); | |
| // Since id enums are not zero-indexed, we need to transform the given id into | |
| // its associated index. | |
| struct BlockIdToIndexFunctor { | |
| using argument_type = unsigned; | |
| unsigned operator()(unsigned ID) const { return ID - BI_FIRST; } | |
| }; | |
| struct RecordIdToIndexFunctor { | |
| using argument_type = unsigned; | |
| unsigned operator()(unsigned ID) const { return ID - RI_FIRST; } | |
| }; | |
| using AbbrevDsc = void (*)(std::shared_ptr<BitCodeAbbrev> &Abbrev); | |
| } // namespace | |
| static void generateAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev, | |
| const std::initializer_list<BitCodeAbbrevOp> Ops) { | |
| for (const auto &Op : Ops) | |
| Abbrev->Add(Op); | |
| } | |
| static void genBoolAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev) { | |
| generateAbbrev( | |
| Abbrev, | |
| {// 0. Boolean | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, BitCodeConstants::BoolSize)}); | |
| } | |
| static void genIntAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev) { | |
| generateAbbrev( | |
| Abbrev, | |
| {// 0. Fixed-size integer | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, BitCodeConstants::IntSize)}); | |
| } | |
| static void genSymbolIdAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev) { | |
| generateAbbrev( | |
| Abbrev, | |
| {// 0. Fixed-size integer (length of the sha1'd USR) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, BitCodeConstants::USRLengthSize), | |
| // 1. Fixed-size array of Char6 (USR) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Array), | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, | |
| BitCodeConstants::USRBitLengthSize)}); | |
| } | |
| static void genStringAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev) { | |
| generateAbbrev(Abbrev, | |
| {// 0. Fixed-size integer (length of the following string) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, | |
| BitCodeConstants::StringLengthSize), | |
| // 1. The string blob | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)}); | |
| } | |
| // Assumes that the file will not have more than 65535 lines. | |
| static void genLocationAbbrev(std::shared_ptr<BitCodeAbbrev> &Abbrev) { | |
| generateAbbrev( | |
| Abbrev, | |
| {// 0. Fixed-size integer (line number) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, | |
| BitCodeConstants::LineNumberSize), | |
| // 1. Fixed-size integer (start line number) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, | |
| BitCodeConstants::LineNumberSize), | |
| // 2. Boolean (IsFileInRootDir) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, BitCodeConstants::BoolSize), | |
| // 3. Fixed-size integer (length of the following string (filename)) | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, | |
| BitCodeConstants::StringLengthSize), | |
| // 4. The string blob | |
| BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)}); | |
| } | |
| namespace { | |
| struct RecordIdDsc { | |
| RecordIdDsc() = default; | |
| constexpr RecordIdDsc(StringRef Name, AbbrevDsc Abbrev) | |
| : Name(Name), Abbrev(Abbrev) {} | |
| // Is this 'description' valid? | |
| explicit operator bool() const { | |
| return Abbrev != nullptr && Name.data() != nullptr && !Name.empty(); | |
| } | |
| StringRef Name; | |
| AbbrevDsc Abbrev = nullptr; | |
| }; | |
| static const IndexedMap<StringRef, BlockIdToIndexFunctor> BlockIdNameMap = | |
| []() { | |
| IndexedMap<StringRef, BlockIdToIndexFunctor> BlockIdNameMap; | |
| BlockIdNameMap.resize(BlockIdCount); | |
| // There is no init-list constructor for the IndexedMap, so have to | |
| // improvise | |
| static constexpr std::pair<BlockId, StringRef> IdToName[] = { | |
| {BI_VERSION_BLOCK_ID, "VersionBlock"}, | |
| {BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"}, | |
| {BI_ENUM_BLOCK_ID, "EnumBlock"}, | |
| {BI_ENUM_VALUE_BLOCK_ID, "EnumValueBlock"}, | |
| {BI_TYPEDEF_BLOCK_ID, "TypedefBlock"}, | |
| {BI_TYPE_BLOCK_ID, "TypeBlock"}, | |
| {BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"}, | |
| {BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"}, | |
| {BI_RECORD_BLOCK_ID, "RecordBlock"}, | |
| {BI_BASE_RECORD_BLOCK_ID, "BaseRecordBlock"}, | |
| {BI_FUNCTION_BLOCK_ID, "FunctionBlock"}, | |
| {BI_COMMENT_BLOCK_ID, "CommentBlock"}, | |
| {BI_REFERENCE_BLOCK_ID, "ReferenceBlock"}, | |
| {BI_TEMPLATE_BLOCK_ID, "TemplateBlock"}, | |
| {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, "TemplateSpecializationBlock"}, | |
| {BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"}, | |
| {BI_CONSTRAINT_BLOCK_ID, "ConstraintBlock"}, | |
| {BI_CONCEPT_BLOCK_ID, "ConceptBlock"}, | |
| {BI_VAR_BLOCK_ID, "VarBlock"}, | |
| {BI_FRIEND_BLOCK_ID, "FriendBlock"}}; | |
| ArrayRef<std::pair<BlockId, StringRef>> Inits(IdToName); | |
| assert(Inits.size() == BlockIdCount); | |
| for (const auto &Init : Inits) | |
| BlockIdNameMap[Init.first] = Init.second; | |
| assert(BlockIdNameMap.size() == BlockIdCount); | |
| return BlockIdNameMap; | |
| }(); | |
| static const IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> RecordIdNameMap = | |
| []() { | |
| IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> RecordIdNameMap; | |
| RecordIdNameMap.resize(RecordIdCount); | |
| // There is no init-list constructor for the IndexedMap, so have to | |
| // improvise | |
| static constexpr std::pair<RecordId, RecordIdDsc> IdToFunc[] = { | |
| {VERSION, {"Version", &genIntAbbrev}}, | |
| {COMMENT_KIND, {"Kind", &genStringAbbrev}}, | |
| {COMMENT_TEXT, {"Text", &genStringAbbrev}}, | |
| {COMMENT_NAME, {"Name", &genStringAbbrev}}, | |
| {COMMENT_DIRECTION, {"Direction", &genStringAbbrev}}, | |
| {COMMENT_PARAMNAME, {"ParamName", &genStringAbbrev}}, | |
| {COMMENT_CLOSENAME, {"CloseName", &genStringAbbrev}}, | |
| {COMMENT_SELFCLOSING, {"SelfClosing", &genBoolAbbrev}}, | |
| {COMMENT_EXPLICIT, {"Explicit", &genBoolAbbrev}}, | |
| {COMMENT_ATTRKEY, {"AttrKey", &genStringAbbrev}}, | |
| {COMMENT_ATTRVAL, {"AttrVal", &genStringAbbrev}}, | |
| {COMMENT_ARG, {"Arg", &genStringAbbrev}}, | |
| {FIELD_TYPE_NAME, {"Name", &genStringAbbrev}}, | |
| {FIELD_DEFAULT_VALUE, {"DefaultValue", &genStringAbbrev}}, | |
| {FIELD_TYPE_IS_BUILTIN, {"IsBuiltin", &genBoolAbbrev}}, | |
| {FIELD_TYPE_IS_TEMPLATE, {"IsTemplate", &genBoolAbbrev}}, | |
| {MEMBER_TYPE_NAME, {"Name", &genStringAbbrev}}, | |
| {MEMBER_TYPE_ACCESS, {"Access", &genIntAbbrev}}, | |
| {MEMBER_TYPE_IS_STATIC, {"IsStatic", &genBoolAbbrev}}, | |
| {MEMBER_TYPE_IS_BUILTIN, {"IsBuiltin", &genBoolAbbrev}}, | |
| {MEMBER_TYPE_IS_TEMPLATE, {"IsTemplate", &genBoolAbbrev}}, | |
| {TYPE_IS_BUILTIN, {"IsBuiltin", &genBoolAbbrev}}, | |
| {TYPE_IS_TEMPLATE, {"IsTemplate", &genBoolAbbrev}}, | |
| {NAMESPACE_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {NAMESPACE_NAME, {"Name", &genStringAbbrev}}, | |
| {NAMESPACE_PATH, {"Path", &genStringAbbrev}}, | |
| {NAMESPACE_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}}, | |
| {ENUM_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {ENUM_NAME, {"Name", &genStringAbbrev}}, | |
| {ENUM_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {ENUM_LOCATION, {"Location", &genLocationAbbrev}}, | |
| {ENUM_SCOPED, {"Scoped", &genBoolAbbrev}}, | |
| {ENUM_VALUE_NAME, {"Name", &genStringAbbrev}}, | |
| {ENUM_VALUE_VALUE, {"Value", &genStringAbbrev}}, | |
| {ENUM_VALUE_EXPR, {"Expr", &genStringAbbrev}}, | |
| {RECORD_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {RECORD_NAME, {"Name", &genStringAbbrev}}, | |
| {RECORD_PATH, {"Path", &genStringAbbrev}}, | |
| {RECORD_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {RECORD_LOCATION, {"Location", &genLocationAbbrev}}, | |
| {RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}}, | |
| {RECORD_IS_TYPE_DEF, {"IsTypeDef", &genBoolAbbrev}}, | |
| {RECORD_MANGLED_NAME, {"MangledName", &genStringAbbrev}}, | |
| {RECORD_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}}, | |
| {BASE_RECORD_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {BASE_RECORD_NAME, {"Name", &genStringAbbrev}}, | |
| {BASE_RECORD_PATH, {"Path", &genStringAbbrev}}, | |
| {BASE_RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}}, | |
| {BASE_RECORD_IS_VIRTUAL, {"IsVirtual", &genBoolAbbrev}}, | |
| {BASE_RECORD_ACCESS, {"Access", &genIntAbbrev}}, | |
| {BASE_RECORD_IS_PARENT, {"IsParent", &genBoolAbbrev}}, | |
| {FUNCTION_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {FUNCTION_NAME, {"Name", &genStringAbbrev}}, | |
| {FUNCTION_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {FUNCTION_LOCATION, {"Location", &genLocationAbbrev}}, | |
| {FUNCTION_ACCESS, {"Access", &genIntAbbrev}}, | |
| {FUNCTION_IS_METHOD, {"IsMethod", &genBoolAbbrev}}, | |
| {FUNCTION_IS_STATIC, {"IsStatic", &genBoolAbbrev}}, | |
| {REFERENCE_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {REFERENCE_NAME, {"Name", &genStringAbbrev}}, | |
| {REFERENCE_QUAL_NAME, {"QualName", &genStringAbbrev}}, | |
| {REFERENCE_TYPE, {"RefType", &genIntAbbrev}}, | |
| {REFERENCE_PATH, {"Path", &genStringAbbrev}}, | |
| {REFERENCE_FIELD, {"Field", &genIntAbbrev}}, | |
| {REFERENCE_FILE, {"File", &genStringAbbrev}}, | |
| {TEMPLATE_PARAM_CONTENTS, {"Contents", &genStringAbbrev}}, | |
| {TEMPLATE_SPECIALIZATION_OF, | |
| {"SpecializationOf", &genSymbolIdAbbrev}}, | |
| {TYPEDEF_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {TYPEDEF_NAME, {"Name", &genStringAbbrev}}, | |
| {TYPEDEF_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {TYPEDEF_IS_USING, {"IsUsing", &genBoolAbbrev}}, | |
| {CONCEPT_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {CONCEPT_NAME, {"Name", &genStringAbbrev}}, | |
| {CONCEPT_IS_TYPE, {"IsType", &genBoolAbbrev}}, | |
| {CONCEPT_CONSTRAINT_EXPRESSION, | |
| {"ConstraintExpression", &genStringAbbrev}}, | |
| {CONCEPT_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {CONSTRAINT_EXPRESSION, {"Expression", &genStringAbbrev}}, | |
| {VAR_USR, {"USR", &genSymbolIdAbbrev}}, | |
| {VAR_NAME, {"Name", &genStringAbbrev}}, | |
| {VAR_DEFLOCATION, {"DefLocation", &genLocationAbbrev}}, | |
| {VAR_IS_STATIC, {"IsStatic", &genBoolAbbrev}}, | |
| {FRIEND_IS_CLASS, {"IsClass", &genBoolAbbrev}}}; | |
| ArrayRef<std::pair<RecordId, RecordIdDsc>> Inits(IdToFunc); | |
| assert(Inits.size() == RecordIdCount); | |
| for (const auto &Init : Inits) { | |
| RecordIdNameMap[Init.first] = Init.second; | |
| assert((Init.second.Name.size() + 1) <= BitCodeConstants::RecordSize); | |
| } | |
| assert(RecordIdNameMap.size() == RecordIdCount); | |
| return RecordIdNameMap; | |
| }(); | |
| struct BlockToIdList { | |
| BlockId BID; | |
| std::initializer_list<RecordId> RIDs; | |
| }; | |
| static const BlockToIdList RecordsByBlock[] = { | |
| // Version Block | |
| {BI_VERSION_BLOCK_ID, {VERSION}}, | |
| // Comment Block | |
| {BI_COMMENT_BLOCK_ID, | |
| {COMMENT_KIND, COMMENT_TEXT, COMMENT_NAME, COMMENT_DIRECTION, | |
| COMMENT_PARAMNAME, COMMENT_CLOSENAME, COMMENT_SELFCLOSING, | |
| COMMENT_EXPLICIT, COMMENT_ATTRKEY, COMMENT_ATTRVAL, COMMENT_ARG}}, | |
| // Type Block | |
| {BI_TYPE_BLOCK_ID, {TYPE_IS_BUILTIN, TYPE_IS_TEMPLATE}}, | |
| // FieldType Block | |
| {BI_FIELD_TYPE_BLOCK_ID, | |
| {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE, FIELD_TYPE_IS_BUILTIN, | |
| FIELD_TYPE_IS_TEMPLATE}}, | |
| // MemberType Block | |
| {BI_MEMBER_TYPE_BLOCK_ID, | |
| {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, MEMBER_TYPE_IS_STATIC, | |
| MEMBER_TYPE_IS_BUILTIN, MEMBER_TYPE_IS_TEMPLATE}}, | |
| // Enum Block | |
| {BI_ENUM_BLOCK_ID, | |
| {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_SCOPED}}, | |
| // Enum Value Block | |
| {BI_ENUM_VALUE_BLOCK_ID, | |
| {ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}}, | |
| // Typedef Block | |
| {BI_TYPEDEF_BLOCK_ID, | |
| {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}}, | |
| // Namespace Block | |
| {BI_NAMESPACE_BLOCK_ID, | |
| {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH, NAMESPACE_PARENT_USR}}, | |
| // Record Block | |
| {BI_RECORD_BLOCK_ID, | |
| {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION, RECORD_LOCATION, | |
| RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF, RECORD_MANGLED_NAME, | |
| RECORD_PARENT_USR}}, | |
| // BaseRecord Block | |
| {BI_BASE_RECORD_BLOCK_ID, | |
| {BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH, BASE_RECORD_TAG_TYPE, | |
| BASE_RECORD_IS_VIRTUAL, BASE_RECORD_ACCESS, BASE_RECORD_IS_PARENT}}, | |
| // Function Block | |
| {BI_FUNCTION_BLOCK_ID, | |
| {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, | |
| FUNCTION_ACCESS, FUNCTION_IS_METHOD, FUNCTION_IS_STATIC}}, | |
| // Reference Block | |
| {BI_REFERENCE_BLOCK_ID, | |
| {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE, | |
| REFERENCE_PATH, REFERENCE_FIELD, REFERENCE_FILE}}, | |
| // Template Blocks. | |
| {BI_TEMPLATE_BLOCK_ID, {}}, | |
| {BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}}, | |
| {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, {TEMPLATE_SPECIALIZATION_OF}}, | |
| // Concept Block | |
| {BI_CONCEPT_BLOCK_ID, | |
| {CONCEPT_USR, CONCEPT_NAME, CONCEPT_IS_TYPE, CONCEPT_CONSTRAINT_EXPRESSION, | |
| CONCEPT_DEFLOCATION}}, | |
| // Constraint Block | |
| {BI_CONSTRAINT_BLOCK_ID, {CONSTRAINT_EXPRESSION}}, | |
| {BI_VAR_BLOCK_ID, {VAR_NAME, VAR_USR, VAR_DEFLOCATION, VAR_IS_STATIC}}, | |
| {BI_FRIEND_BLOCK_ID, {FRIEND_IS_CLASS}}}; | |
| } // namespace | |
| namespace clang { | |
| namespace doc { | |
| // AbbreviationMap | |
| void ClangDocBitcodeWriter::AbbreviationMap::add(RecordId RID, | |
| unsigned AbbrevID) { | |
| assert(RecordIdNameMap[RID] && "Unknown RecordId."); | |
| assert(!Abbrevs.contains(RID) && "Abbreviation already added."); | |
| Abbrevs[RID] = AbbrevID; | |
| } | |
| unsigned ClangDocBitcodeWriter::AbbreviationMap::get(RecordId RID) const { | |
| assert(RecordIdNameMap[RID] && "Unknown RecordId."); | |
| assert(Abbrevs.contains(RID) && "Unknown abbreviation."); | |
| return Abbrevs.lookup(RID); | |
| } | |
| // Validation and Overview Blocks | |
| /// Emits the magic number header to check that its the right format, | |
| /// in this case, 'DOCS'. | |
| void ClangDocBitcodeWriter::emitHeader() { | |
| for (char C : BitCodeConstants::Signature) | |
| Stream.Emit((unsigned)C, BitCodeConstants::SignatureBitSize); | |
| } | |
| void ClangDocBitcodeWriter::emitVersionBlock() { | |
| StreamSubBlockGuard Block(Stream, BI_VERSION_BLOCK_ID); | |
| emitRecord(VersionNumber, VERSION); | |
| } | |
| /// Emits a block ID and the block name to the BLOCKINFO block. | |
| void ClangDocBitcodeWriter::emitBlockID(BlockId BID) { | |
| const auto &BlockIdName = BlockIdNameMap[BID]; | |
| assert(BlockIdName.data() && BlockIdName.size() && "Unknown BlockId."); | |
| Record.clear(); | |
| Record.push_back(BID); | |
| Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, Record); | |
| Stream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, | |
| ArrayRef<unsigned char>(BlockIdName.bytes_begin(), | |
| BlockIdName.bytes_end())); | |
| } | |
| /// Emits a record name to the BLOCKINFO block. | |
| void ClangDocBitcodeWriter::emitRecordID(RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| prepRecordData(ID); | |
| Record.append(RecordIdNameMap[ID].Name.begin(), | |
| RecordIdNameMap[ID].Name.end()); | |
| Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); | |
| } | |
| // Abbreviations | |
| void ClangDocBitcodeWriter::emitAbbrev(RecordId ID, BlockId Block) { | |
| assert(RecordIdNameMap[ID] && "Unknown abbreviation."); | |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); | |
| Abbrev->Add(BitCodeAbbrevOp(ID)); | |
| RecordIdNameMap[ID].Abbrev(Abbrev); | |
| Abbrevs.add(ID, Stream.EmitBlockInfoAbbrev(Block, std::move(Abbrev))); | |
| } | |
| // Records | |
| void ClangDocBitcodeWriter::emitRecord(const SymbolID &Sym, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genSymbolIdAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, Sym != EmptySID)) | |
| return; | |
| assert(Sym.size() == 20); | |
| Record.push_back(Sym.size()); | |
| Record.append(Sym.begin(), Sym.end()); | |
| Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(StringRef Str, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genStringAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, !Str.empty())) | |
| return; | |
| assert(Str.size() < (1U << BitCodeConstants::StringLengthSize)); | |
| Record.push_back(Str.size()); | |
| Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Str); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genLocationAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, true)) | |
| return; | |
| // FIXME: Assert that the line number is of the appropriate size. | |
| Record.push_back(Loc.StartLineNumber); | |
| Record.push_back(Loc.EndLineNumber); | |
| assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize)); | |
| Record.push_back(Loc.IsFileInRootDir); | |
| Record.push_back(Loc.Filename.size()); | |
| Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Loc.Filename); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(bool Val, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genBoolAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, Val)) | |
| return; | |
| Record.push_back(Val); | |
| Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(int Val, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genIntAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, Val)) | |
| return; | |
| // FIXME: Assert that the integer is of the appropriate size. | |
| Record.push_back(Val); | |
| Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(unsigned Val, RecordId ID) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| assert(RecordIdNameMap[ID].Abbrev == &genIntAbbrev && | |
| "Abbrev type mismatch."); | |
| if (!prepRecordData(ID, Val)) | |
| return; | |
| assert(Val < (1U << BitCodeConstants::IntSize)); | |
| Record.push_back(Val); | |
| Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record); | |
| } | |
| void ClangDocBitcodeWriter::emitRecord(const TemplateInfo &Templ) {} | |
| bool ClangDocBitcodeWriter::prepRecordData(RecordId ID, bool ShouldEmit) { | |
| assert(RecordIdNameMap[ID] && "Unknown RecordId."); | |
| if (!ShouldEmit) | |
| return false; | |
| Record.clear(); | |
| Record.push_back(ID); | |
| return true; | |
| } | |
| // BlockInfo Block | |
| void ClangDocBitcodeWriter::emitBlockInfoBlock() { | |
| Stream.EnterBlockInfoBlock(); | |
| for (const auto &Block : RecordsByBlock) { | |
| assert(Block.RIDs.size() < (1U << BitCodeConstants::SubblockIDSize)); | |
| emitBlockInfo(Block.BID, Block.RIDs); | |
| } | |
| Stream.ExitBlock(); | |
| } | |
| void ClangDocBitcodeWriter::emitBlockInfo(BlockId BID, | |
| ArrayRef<RecordId> RIDs) { | |
| assert(RIDs.size() < (1U << BitCodeConstants::SubblockIDSize)); | |
| emitBlockID(BID); | |
| for (RecordId RID : RIDs) { | |
| emitRecordID(RID); | |
| emitAbbrev(RID, BID); | |
| } | |
| } | |
| // Block emission | |
| void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) { | |
| if (R.USR == EmptySID && R.Name.empty()) | |
| return; | |
| StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID); | |
| emitRecord(R.USR, REFERENCE_USR); | |
| emitRecord(R.Name, REFERENCE_NAME); | |
| emitRecord(R.QualName, REFERENCE_QUAL_NAME); | |
| emitRecord((unsigned)R.RefType, REFERENCE_TYPE); | |
| emitRecord(R.Path, REFERENCE_PATH); | |
| emitRecord((unsigned)Field, REFERENCE_FIELD); | |
| emitRecord(R.DocumentationFileName, REFERENCE_FILE); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const FriendInfo &R) { | |
| StreamSubBlockGuard Block(Stream, BI_FRIEND_BLOCK_ID); | |
| emitBlock(R.Ref, FieldId::F_friend); | |
| emitRecord(R.IsClass, FRIEND_IS_CLASS); | |
| if (R.Template) | |
| emitBlock(*R.Template); | |
| for (const auto &P : R.Params) | |
| emitBlock(P); | |
| if (R.ReturnType) | |
| emitBlock(*R.ReturnType); | |
| for (const auto &CI : R.Description) | |
| emitBlock(*CI.Ptr); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_TYPE_BLOCK_ID); | |
| emitBlock(T.Type, FieldId::F_type); | |
| emitRecord(T.IsBuiltIn, TYPE_IS_BUILTIN); | |
| emitRecord(T.IsTemplate, TYPE_IS_TEMPLATE); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID); | |
| emitRecord(T.USR, TYPEDEF_USR); | |
| emitRecord(T.Name, TYPEDEF_NAME); | |
| for (const auto &N : T.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : T.Description) | |
| emitBlock(*CI.Ptr); | |
| if (T.DefLoc) | |
| emitRecord(*T.DefLoc, TYPEDEF_DEFLOCATION); | |
| if (T.Template) | |
| emitBlock(*T.Template); | |
| emitRecord(T.IsUsing, TYPEDEF_IS_USING); | |
| emitBlock(T.Underlying); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID); | |
| emitBlock(T.Type, FieldId::F_type); | |
| emitRecord(T.Name, FIELD_TYPE_NAME); | |
| emitRecord(T.DefaultValue, FIELD_DEFAULT_VALUE); | |
| emitRecord(T.IsBuiltIn, FIELD_TYPE_IS_BUILTIN); | |
| emitRecord(T.IsTemplate, FIELD_TYPE_IS_TEMPLATE); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_MEMBER_TYPE_BLOCK_ID); | |
| emitBlock(T.Type, FieldId::F_type); | |
| emitRecord(T.Name, MEMBER_TYPE_NAME); | |
| emitRecord(T.Access, MEMBER_TYPE_ACCESS); | |
| emitRecord(T.IsStatic, MEMBER_TYPE_IS_STATIC); | |
| emitRecord(T.IsBuiltIn, MEMBER_TYPE_IS_BUILTIN); | |
| emitRecord(T.IsTemplate, MEMBER_TYPE_IS_TEMPLATE); | |
| for (const auto &CI : T.Description) | |
| emitBlock(*CI.Ptr); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const CommentInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_COMMENT_BLOCK_ID); | |
| // Handle Kind (enum) separately, since it is not a string. | |
| emitRecord(commentKindToString(I.Kind), COMMENT_KIND); | |
| for (const auto &L : std::vector<std::pair<StringRef, RecordId>>{ | |
| {I.Text, COMMENT_TEXT}, | |
| {I.Name, COMMENT_NAME}, | |
| {I.Direction, COMMENT_DIRECTION}, | |
| {I.ParamName, COMMENT_PARAMNAME}, | |
| {I.CloseName, COMMENT_CLOSENAME}}) | |
| emitRecord(L.first, L.second); | |
| emitRecord(I.SelfClosing, COMMENT_SELFCLOSING); | |
| emitRecord(I.Explicit, COMMENT_EXPLICIT); | |
| for (const auto &A : I.AttrKeys) | |
| emitRecord(A, COMMENT_ATTRKEY); | |
| for (const auto &A : I.AttrValues) | |
| emitRecord(A, COMMENT_ATTRVAL); | |
| for (const auto &A : I.Args) | |
| emitRecord(A, COMMENT_ARG); | |
| for (const auto &C : I.Children) | |
| emitBlock(C); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_NAMESPACE_BLOCK_ID); | |
| emitRecord(I.USR, NAMESPACE_USR); | |
| emitRecord(I.Name, NAMESPACE_NAME); | |
| emitRecord(I.Path, NAMESPACE_PATH); | |
| emitRecord(I.ParentUSR, NAMESPACE_PARENT_USR); | |
| for (const auto &N : I.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| for (const auto &C : I.Children.Namespaces) | |
| emitBlock(C, FieldId::F_child_namespace); | |
| for (const auto &C : I.Children.Records) | |
| emitBlock(C, FieldId::F_child_record); | |
| for (const auto &C : I.Children.Functions) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Enums) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Typedefs) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Concepts) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Variables) | |
| emitBlock(C); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_ENUM_BLOCK_ID); | |
| emitRecord(I.USR, ENUM_USR); | |
| emitRecord(I.Name, ENUM_NAME); | |
| for (const auto &N : I.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| if (I.DefLoc) | |
| emitRecord(*I.DefLoc, ENUM_DEFLOCATION); | |
| for (const auto &L : I.Loc) | |
| emitRecord(L, ENUM_LOCATION); | |
| emitRecord(I.Scoped, ENUM_SCOPED); | |
| if (I.BaseType) | |
| emitBlock(*I.BaseType); | |
| for (const auto &N : I.Members) | |
| emitBlock(N); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_ENUM_VALUE_BLOCK_ID); | |
| emitRecord(I.Name, ENUM_VALUE_NAME); | |
| emitRecord(I.Value, ENUM_VALUE_VALUE); | |
| emitRecord(I.ValueExpr, ENUM_VALUE_EXPR); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID); | |
| emitRecord(I.USR, RECORD_USR); | |
| emitRecord(I.Name, RECORD_NAME); | |
| emitRecord(I.Path, RECORD_PATH); | |
| emitRecord(I.MangledName, RECORD_MANGLED_NAME); | |
| emitRecord(I.ParentUSR, RECORD_PARENT_USR); | |
| for (const auto &N : I.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| if (I.DefLoc) | |
| emitRecord(*I.DefLoc, RECORD_DEFLOCATION); | |
| for (const auto &L : I.Loc) | |
| emitRecord(L, RECORD_LOCATION); | |
| emitRecord(llvm::to_underlying(I.TagType), RECORD_TAG_TYPE); | |
| emitRecord(I.IsTypeDef, RECORD_IS_TYPE_DEF); | |
| for (const auto &N : I.Members) | |
| emitBlock(N); | |
| for (const auto &P : I.Parents) | |
| emitBlock(P, FieldId::F_parent); | |
| for (const auto &P : I.VirtualParents) | |
| emitBlock(P, FieldId::F_vparent); | |
| for (const auto &PB : I.Bases) | |
| emitBlock(PB); | |
| for (const auto &C : I.Children.Records) | |
| emitBlock(C, FieldId::F_child_record); | |
| for (const auto &C : I.Children.Functions) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Enums) | |
| emitBlock(C); | |
| for (const auto &C : I.Children.Typedefs) | |
| emitBlock(C); | |
| if (I.Template) | |
| emitBlock(*I.Template); | |
| for (const auto &C : I.Friends) | |
| emitBlock(C); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_BASE_RECORD_BLOCK_ID); | |
| emitRecord(I.USR, BASE_RECORD_USR); | |
| emitRecord(I.Name, BASE_RECORD_NAME); | |
| emitRecord(I.Path, BASE_RECORD_PATH); | |
| emitRecord(llvm::to_underlying(I.TagType), BASE_RECORD_TAG_TYPE); | |
| emitRecord(I.IsVirtual, BASE_RECORD_IS_VIRTUAL); | |
| emitRecord(I.Access, BASE_RECORD_ACCESS); | |
| emitRecord(I.IsParent, BASE_RECORD_IS_PARENT); | |
| for (const auto &M : I.Members) | |
| emitBlock(M); | |
| for (const auto &C : I.Children.Functions) | |
| emitBlock(C); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_FUNCTION_BLOCK_ID); | |
| emitRecord(I.USR, FUNCTION_USR); | |
| emitRecord(I.Name, FUNCTION_NAME); | |
| for (const auto &N : I.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| emitRecord(I.Access, FUNCTION_ACCESS); | |
| emitRecord(I.IsMethod, FUNCTION_IS_METHOD); | |
| emitRecord(I.IsStatic, FUNCTION_IS_STATIC); | |
| if (I.DefLoc) | |
| emitRecord(*I.DefLoc, FUNCTION_DEFLOCATION); | |
| for (const auto &L : I.Loc) | |
| emitRecord(L, FUNCTION_LOCATION); | |
| emitBlock(I.Parent, FieldId::F_parent); | |
| emitBlock(I.ReturnType); | |
| for (const auto &N : I.Params) | |
| emitBlock(N); | |
| if (I.Template) | |
| emitBlock(*I.Template); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const ConceptInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_CONCEPT_BLOCK_ID); | |
| emitRecord(I.USR, CONCEPT_USR); | |
| emitRecord(I.Name, CONCEPT_NAME); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| emitRecord(I.IsType, CONCEPT_IS_TYPE); | |
| emitRecord(I.ConstraintExpression, CONCEPT_CONSTRAINT_EXPRESSION); | |
| emitBlock(I.Template); | |
| if (I.DefLoc) | |
| emitRecord(*I.DefLoc, CONCEPT_DEFLOCATION); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const TemplateInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_TEMPLATE_BLOCK_ID); | |
| for (const auto &P : T.Params) | |
| emitBlock(P); | |
| if (T.Specialization) | |
| emitBlock(*T.Specialization); | |
| for (const auto &C : T.Constraints) | |
| emitBlock(C); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const TemplateSpecializationInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_TEMPLATE_SPECIALIZATION_BLOCK_ID); | |
| emitRecord(T.SpecializationOf, TEMPLATE_SPECIALIZATION_OF); | |
| for (const auto &P : T.Params) | |
| emitBlock(P); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const TemplateParamInfo &T) { | |
| StreamSubBlockGuard Block(Stream, BI_TEMPLATE_PARAM_BLOCK_ID); | |
| emitRecord(T.Contents, TEMPLATE_PARAM_CONTENTS); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const ConstraintInfo &C) { | |
| StreamSubBlockGuard Block(Stream, BI_CONSTRAINT_BLOCK_ID); | |
| emitRecord(C.ConstraintExpr, CONSTRAINT_EXPRESSION); | |
| emitBlock(C.ConceptRef, FieldId::F_concept); | |
| } | |
| void ClangDocBitcodeWriter::emitBlock(const VarInfo &I) { | |
| StreamSubBlockGuard Block(Stream, BI_VAR_BLOCK_ID); | |
| emitRecord(I.USR, VAR_USR); | |
| emitRecord(I.Name, VAR_NAME); | |
| for (const auto &N : I.Namespace) | |
| emitBlock(N, FieldId::F_namespace); | |
| for (const auto &CI : I.Description) | |
| emitBlock(*CI.Ptr); | |
| if (I.DefLoc) | |
| emitRecord(*I.DefLoc, VAR_DEFLOCATION); | |
| emitRecord(I.IsStatic, VAR_IS_STATIC); | |
| emitBlock(I.Type); | |
| } | |
| bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) { | |
| switch (I->IT) { | |
| case InfoType::IT_namespace: | |
| emitBlock(*cast<NamespaceInfo>(I)); | |
| break; | |
| case InfoType::IT_record: | |
| emitBlock(*cast<RecordInfo>(I)); | |
| break; | |
| case InfoType::IT_enum: | |
| emitBlock(*cast<EnumInfo>(I)); | |
| break; | |
| case InfoType::IT_function: | |
| emitBlock(*cast<FunctionInfo>(I)); | |
| break; | |
| case InfoType::IT_typedef: | |
| emitBlock(*cast<TypedefInfo>(I)); | |
| break; | |
| case InfoType::IT_concept: | |
| emitBlock(*cast<ConceptInfo>(I)); | |
| break; | |
| case InfoType::IT_variable: | |
| emitBlock(*cast<VarInfo>(I)); | |
| break; | |
| case InfoType::IT_friend: | |
| emitBlock(*cast<FriendInfo>(I)); | |
| break; | |
| case InfoType::IT_default: | |
| unsigned ID = Diags.getCustomDiagID(DiagnosticsEngine::Error, | |
| "Unexpected info, unable to write."); | |
| Diags.Report(ID); | |
| return true; | |
| } | |
| return false; | |
| } | |
| } // namespace doc | |
| } // namespace clang |