nridge created this revision.
nridge added a reviewer: kadircet.
Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay, 
ilya-biryukov.
Herald added a project: clang.

This builds on D59407 <https://reviews.llvm.org/D59407> to provide YAML and 
RIFF serialization support.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D62459

Files:
  clang-tools-extra/clangd/index/Serialization.cpp
  clang-tools-extra/clangd/index/Serialization.h
  clang-tools-extra/clangd/index/YAMLSerialization.cpp
  clang-tools-extra/clangd/unittests/SerializationTests.cpp

Index: clang-tools-extra/clangd/unittests/SerializationTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SerializationTests.cpp
+++ clang-tools-extra/clangd/unittests/SerializationTests.cpp
@@ -149,8 +149,15 @@
 }
 std::vector<std::string> YAMLFromRefs(const RefSlab &Slab) {
   std::vector<std::string> Result;
-  for (const auto &Sym : Slab)
-    Result.push_back(toYAML(Sym));
+  for (const auto &Refs : Slab)
+    Result.push_back(toYAML(Refs));
+  return Result;
+}
+
+std::vector<std::string> YAMLFromRelations(const RelationSlab &Slab) {
+  std::vector<std::string> Result;
+  for (const auto &Rel : Slab)
+    Result.push_back(toYAML(Rel));
   return Result;
 }
 
@@ -215,6 +222,110 @@
   }
 }
 
+const char *RelationsYAML = R"(
+--- !Symbol
+ID:              6481EE7AF2841756
+Name:            Base
+Scope:           ''
+SymInfo:         
+  Kind:            Struct
+  Lang:            C
+CanonicalDeclaration: 
+  FileURI:         'file:///path/foo.cc'
+  Start:           
+    Line:            0
+    Column:          7
+  End:             
+    Line:            0
+    Column:          11
+Definition:      
+  FileURI:         'file:///path/foo.cc'
+  Start:           
+    Line:            0
+    Column:          7
+  End:             
+    Line:            0
+    Column:          11
+References:      1
+Origin:          4
+Flags:           0
+Signature:       ''
+TemplateSpecializationArgs: ''
+CompletionSnippetSuffix: ''
+Documentation:   ''
+ReturnType:      ''
+Type:            ''
+...
+--- !Symbol
+ID:              6512AEC512EA3A2D
+Name:            Derived
+Scope:           ''
+SymInfo:
+  Kind:            Struct
+  Lang:            Cpp
+CanonicalDeclaration:
+  FileURI:         'file:///path/foo.cc'
+  Start:
+    Line:            1
+    Column:          7
+  End:
+    Line:            1
+    Column:          14
+Definition:
+  FileURI:         'file:///path/foo.cc'
+  Start:
+    Line:            1
+    Column:          7
+  End:
+    Line:            1
+    Column:          14
+Origin:          4
+Flags:           0
+Signature:       ''
+TemplateSpecializationArgs: ''
+CompletionSnippetSuffix: ''
+Documentation:   ''
+ReturnType:      ''
+Type:            ''
+...
+--- !Relations
+Subject:
+  ID:              6481EE7AF2841756
+Predicate:       2
+Object:
+  ID:              6512AEC512EA3A2D
+...
+)";
+
+TEST(SerializationTest, Relations) {
+  auto In = readIndexFile(RelationsYAML);
+
+  EXPECT_TRUE(bool(In)) << In.takeError();
+
+  SymbolID Base = cantFail(SymbolID::fromStr("6481EE7AF2841756"));
+  SymbolID Derived = cantFail(SymbolID::fromStr("6512AEC512EA3A2D"));
+
+  EXPECT_TRUE(bool(In->Relations));
+  EXPECT_THAT(*In->Relations,
+              UnorderedElementsAre(
+                  Relation{Base, index::SymbolRole::RelationBaseOf, Derived}));
+
+  // Write to binary format, and parse again.
+  IndexFileOut Out(*In);
+  Out.Format = IndexFileFormat::RIFF;
+  std::string Serialized = llvm::to_string(Out);
+
+  auto In2 = readIndexFile(Serialized);
+  ASSERT_TRUE(bool(In2)) << In.takeError();
+  ASSERT_TRUE(In2->Symbols);
+  ASSERT_TRUE(In2->Refs);
+  ASSERT_TRUE(In2->Relations);
+
+  // Assert the YAML serializations match, for nice comparisons and diffs.
+  EXPECT_THAT(YAMLFromRelations(*In2->Relations),
+              UnorderedElementsAreArray(YAMLFromRelations(*In->Relations)));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/index/YAMLSerialization.cpp
===================================================================
--- clang-tools-extra/clangd/index/YAMLSerialization.cpp
+++ clang-tools-extra/clangd/index/YAMLSerialization.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Index.h"
+#include "Relation.h"
 #include "Serialization.h"
 #include "SymbolLocation.h"
 #include "SymbolOrigin.h"
@@ -35,10 +36,11 @@
 namespace {
 using RefBundle =
     std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>;
-// This is a pale imitation of std::variant<Symbol, RefBundle>
+// This is a pale imitation of std::variant<Symbol, RefBundle, Relation>
 struct VariantEntry {
   llvm::Optional<clang::clangd::Symbol> Symbol;
   llvm::Optional<RefBundle> Refs;
+  llvm::Optional<clang::clangd::Relation> Relation;
 };
 // A class helps YAML to serialize the 32-bit encoded position (Line&Column),
 // as YAMLIO can't directly map bitfields.
@@ -53,6 +55,8 @@
 
 using clang::clangd::Ref;
 using clang::clangd::RefKind;
+using clang::clangd::Relation;
+using clang::clangd::RelationKind;
 using clang::clangd::Symbol;
 using clang::clangd::SymbolID;
 using clang::clangd::SymbolLocation;
@@ -60,6 +64,7 @@
 using clang::index::SymbolInfo;
 using clang::index::SymbolKind;
 using clang::index::SymbolLanguage;
+using clang::index::SymbolRole;
 
 // Helper to (de)serialize the SymbolID. We serialize it as a hex string.
 struct NormalizedSymbolID {
@@ -275,6 +280,47 @@
   }
 };
 
+struct NormalizedSymbolRole {
+  NormalizedSymbolRole(IO &) {}
+  NormalizedSymbolRole(IO &IO, SymbolRole R) {
+    auto K = clang::clangd::symbolRoleToRelationKind(R);
+    if (K) {
+      Kind = static_cast<uint8_t>(*K);
+    } else {
+      IO.setError("invalid relation kind");
+    }
+  }
+
+  SymbolRole denormalize(IO &IO) {
+    auto R = clang::clangd::relationKindToSymbolRole(
+        static_cast<RelationKind>(Kind));
+    if (R) {
+      return *R;
+    }
+    IO.setError("invalid relation kind");
+    return SymbolRole();
+  }
+
+  uint8_t Kind = 0;
+};
+
+template <> struct MappingTraits<SymbolID> {
+  static void mapping(IO &IO, SymbolID &ID) {
+    MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, ID);
+    IO.mapRequired("ID", NSymbolID->HexString);
+  }
+};
+
+template <> struct MappingTraits<Relation> {
+  static void mapping(IO &IO, Relation &Relation) {
+    MappingNormalization<NormalizedSymbolRole, SymbolRole> NRole(
+        IO, Relation.Predicate);
+    IO.mapRequired("Subject", Relation.Subject);
+    IO.mapRequired("Predicate", NRole->Kind);
+    IO.mapRequired("Object", Relation.Object);
+  }
+};
+
 template <> struct MappingTraits<VariantEntry> {
   static void mapping(IO &IO, VariantEntry &Variant) {
     if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) {
@@ -285,6 +331,10 @@
       if (!IO.outputting())
         Variant.Refs.emplace();
       MappingTraits<RefBundle>::mapping(IO, *Variant.Refs);
+    } else if (IO.mapTag("!Relations", Variant.Relation.hasValue())) {
+      if (!IO.outputting())
+        Variant.Relation.emplace();
+      MappingTraits<Relation>::mapping(IO, *Variant.Relation);
     }
   }
 };
@@ -308,11 +358,18 @@
       Entry.Refs = Sym;
       Yout << Entry;
     }
+  if (O.Relations)
+    for (auto &R : *O.Relations) {
+      VariantEntry Entry;
+      Entry.Relation = R;
+      Yout << Entry;
+    }
 }
 
 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) {
   SymbolSlab::Builder Symbols;
   RefSlab::Builder Refs;
+  RelationSlab::Builder Relations;
   llvm::BumpPtrAllocator
       Arena; // store the underlying data of Position::FileURI.
   llvm::UniqueStringSaver Strings(Arena);
@@ -329,12 +386,15 @@
     if (Variant.Refs)
       for (const auto &Ref : Variant.Refs->second)
         Refs.insert(Variant.Refs->first, Ref);
+    if (Variant.Relation)
+      Relations.insert(*Variant.Relation);
     Yin.nextDocument();
   }
 
   IndexFileIn Result;
   Result.Symbols.emplace(std::move(Symbols).build());
   Result.Refs.emplace(std::move(Refs).build());
+  Result.Relations.emplace(std::move(Relations).build());
   return std::move(Result);
 }
 
@@ -360,5 +420,16 @@
   return Buf;
 }
 
+std::string toYAML(const Relation &R) {
+  std::string Buf;
+  {
+    llvm::raw_string_ostream OS(Buf);
+    llvm::yaml::Output Yout(OS);
+    Relation Rel = R; // copy: Yout<< requires mutability.
+    Yout << Rel;
+  }
+  return Buf;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/index/Serialization.h
===================================================================
--- clang-tools-extra/clangd/index/Serialization.h
+++ clang-tools-extra/clangd/index/Serialization.h
@@ -41,6 +41,7 @@
 struct IndexFileIn {
   llvm::Optional<SymbolSlab> Symbols;
   llvm::Optional<RefSlab> Refs;
+  llvm::Optional<RelationSlab> Relations;
   // Keys are URIs of the source files.
   llvm::Optional<IncludeGraph> Sources;
 };
@@ -51,6 +52,7 @@
 struct IndexFileOut {
   const SymbolSlab *Symbols = nullptr;
   const RefSlab *Refs = nullptr;
+  const RelationSlab *Relations = nullptr;
   // Keys are URIs of the source files.
   const IncludeGraph *Sources = nullptr;
   // TODO: Support serializing Dex posting lists.
@@ -59,7 +61,8 @@
   IndexFileOut() = default;
   IndexFileOut(const IndexFileIn &I)
       : Symbols(I.Symbols ? I.Symbols.getPointer() : nullptr),
-        Refs(I.Refs ? I.Refs.getPointer() : nullptr) {}
+        Refs(I.Refs ? I.Refs.getPointer() : nullptr),
+        Relations(I.Relations ? I.Relations.getPointer() : nullptr) {}
 };
 // Serializes an index file.
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O);
@@ -67,12 +70,18 @@
 // Convert a single symbol to YAML, a nice debug representation.
 std::string toYAML(const Symbol &);
 std::string toYAML(const std::pair<SymbolID, ArrayRef<Ref>> &);
+std::string toYAML(const Relation &);
 
 // Build an in-memory static index from an index file.
 // The size should be relatively small, so data can be managed in memory.
 std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef Filename,
                                        bool UseDex = true);
 
+// Used for serializing SymbolRole as used in Relation.
+enum class RelationKind : uint8_t { ChildOf = 1, BaseOf };
+llvm::Expected<RelationKind> symbolRoleToRelationKind(index::SymbolRole);
+llvm::Expected<index::SymbolRole> relationKindToSymbolRole(RelationKind);
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/index/Serialization.cpp
===================================================================
--- clang-tools-extra/clangd/index/Serialization.cpp
+++ clang-tools-extra/clangd/index/Serialization.cpp
@@ -24,6 +24,37 @@
   return llvm::make_error<llvm::StringError>(Msg,
                                              llvm::inconvertibleErrorCode());
 }
+} // namespace
+
+llvm::Expected<RelationKind> symbolRoleToRelationKind(index::SymbolRole Role) {
+  // SymbolRole is used to record relations in the index.
+  // Only handle the relations we actually store currently.
+  // If we start storing more relations, this list can be expanded.
+  switch (Role) {
+  case index::SymbolRole::RelationChildOf: {
+    return RelationKind::ChildOf;
+  }
+  case index::SymbolRole::RelationBaseOf: {
+    return RelationKind::BaseOf;
+  }
+  default:
+    return makeError("invalid relation kind");
+  }
+}
+
+llvm::Expected<index::SymbolRole> relationKindToSymbolRole(RelationKind Kind) {
+  switch (Kind) {
+  case RelationKind::ChildOf: {
+    return index::SymbolRole::RelationChildOf;
+  }
+  case RelationKind::BaseOf: {
+    return index::SymbolRole::RelationBaseOf;
+  }
+  }
+  return makeError("invalid relation kind");
+}
+
+namespace {
 
 // IO PRIMITIVES
 // We use little-endian 32 bit ints, sometimes with variable-length encoding.
@@ -44,6 +75,8 @@
   // The "error" bit is set by reading past EOF or reading invalid data.
   // When in an error state, reads may return zero values: callers should check.
   bool err() const { return Err; }
+  // A caller can set the error bit if an invalid value was read.
+  void setErr() { Err = true; }
   // Did we read all the data, or encounter an error?
   bool eof() const { return Begin == End || Err; }
   // All the data we didn't read yet.
@@ -358,6 +391,32 @@
   return Result;
 }
 
+// RELATIONS ENCODING
+// A relations section is a flat list of relations. Each relation has:
+//  - SymbolID (subject): 8 bytes
+//  - relation kind (predicate): 1 byte
+//  - SymbolID (object): 8 bytes
+
+void writeRelation(const Relation &R, llvm::raw_ostream &OS) {
+  OS << R.Subject.raw();
+  RelationKind Kind = cantFail(symbolRoleToRelationKind(R.Predicate));
+  OS.write(static_cast<uint8_t>(Kind));
+  OS << R.Object.raw();
+}
+
+Relation readRelation(Reader &Data) {
+  SymbolID Subject = Data.consumeID();
+  index::SymbolRole Predicate{};
+  if (auto Role = relationKindToSymbolRole(
+          static_cast<RelationKind>(Data.consume8()))) {
+    Predicate = *Role;
+  } else {
+    Data.setErr();
+  }
+  SymbolID Object = Data.consumeID();
+  return {Subject, Predicate, Object};
+}
+
 // FILE ENCODING
 // A file is a RIFF chunk with type 'CdIx'.
 // It contains the sections:
@@ -434,6 +493,17 @@
       return makeError("malformed or truncated refs");
     Result.Refs = std::move(Refs).build();
   }
+  if (Chunks.count("rela")) {
+    Reader RelationsReader(Chunks.lookup("rela"));
+    RelationSlab::Builder Relations;
+    while (!RelationsReader.eof()) {
+      auto Relation = readRelation(RelationsReader);
+      Relations.insert(Relation);
+    }
+    if (RelationsReader.err())
+      return makeError("malformed or truncated relations");
+    Result.Relations = std::move(Relations).build();
+  }
   return std::move(Result);
 }
 
@@ -483,6 +553,14 @@
     }
   }
 
+  std::vector<Relation> Relations;
+  if (Data.Relations) {
+    for (const auto &Relation : *Data.Relations) {
+      Relations.emplace_back(Relation);
+      // No strings to be interned in relations.
+    }
+  }
+
   std::string StringSection;
   {
     llvm::raw_string_ostream StringOS(StringSection);
@@ -508,6 +586,16 @@
     RIFF.Chunks.push_back({riff::fourCC("refs"), RefsSection});
   }
 
+  std::string RelationSection;
+  if (Data.Relations) {
+    {
+      llvm::raw_string_ostream RelationOS{RelationSection};
+      for (const auto &Relation : Relations)
+        writeRelation(Relation, RelationOS);
+    }
+    RIFF.Chunks.push_back({riff::fourCC("rela"), RelationSection});
+  }
+
   std::string SrcsSection;
   {
     {
@@ -561,6 +649,7 @@
 
   SymbolSlab Symbols;
   RefSlab Refs;
+  RelationSlab Relations;
   {
     trace::Span Tracer("ParseIndex");
     if (auto I = readIndexFile(Buffer->get()->getBuffer())) {
@@ -568,6 +657,8 @@
         Symbols = std::move(*I->Symbols);
       if (I->Refs)
         Refs = std::move(*I->Refs);
+      if (I->Relations)
+        Relations = std::move(*I->Relations);
     } else {
       llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n";
       return nullptr;
@@ -576,15 +667,17 @@
 
   size_t NumSym = Symbols.size();
   size_t NumRefs = Refs.numRefs();
+  size_t NumRelations = Relations.size();
 
   trace::Span Tracer("BuildIndex");
   auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs))
                       : MemIndex::build(std::move(Symbols), std::move(Refs));
   vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n"
        "  - number of symbols: {3}\n"
-       "  - number of refs: {4}\n",
+       "  - number of refs: {4}\n"
+       "  - numnber of relations: {5}",
        UseDex ? "Dex" : "MemIndex", SymbolFilename,
-       Index->estimateMemoryUsage(), NumSym, NumRefs);
+       Index->estimateMemoryUsage(), NumSym, NumRefs, NumRelations);
   return Index;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to