1 //===--- SymbolYAML.cpp ------------------------------------------*- C++-*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // A YAML index file is a sequence of tagged entries.
11 // Each entry either encodes a Symbol or the list of references to a symbol
12 // (a "ref bundle").
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "Index.h"
17 #include "Serialization.h"
18 #include "Trace.h"
19 #include "dex/Dex.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/Support/Allocator.h"
24 #include "llvm/Support/Errc.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/StringSaver.h"
27 #include "llvm/Support/YAMLTraits.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <cstdint>
30 
31 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences)
32 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref)
33 
34 namespace {
35 using RefBundle =
36     std::pair<clang::clangd::SymbolID, std::vector<clang::clangd::Ref>>;
37 // This is a pale imitation of std::variant<Symbol, RefBundle>
38 struct VariantEntry {
39   llvm::Optional<clang::clangd::Symbol> Symbol;
40   llvm::Optional<RefBundle> Refs;
41 };
42 // A class helps YAML to serialize the 32-bit encoded position (Line&Column),
43 // as YAMLIO can't directly map bitfields.
44 struct YPosition {
45   uint32_t Line;
46   uint32_t Column;
47 };
48 
49 } // namespace
50 namespace llvm {
51 namespace yaml {
52 
53 using clang::clangd::Ref;
54 using clang::clangd::RefKind;
55 using clang::clangd::Symbol;
56 using clang::clangd::SymbolID;
57 using clang::clangd::SymbolLocation;
58 using clang::clangd::SymbolOrigin;
59 using clang::index::SymbolInfo;
60 using clang::index::SymbolKind;
61 using clang::index::SymbolLanguage;
62 
63 // Helper to (de)serialize the SymbolID. We serialize it as a hex string.
64 struct NormalizedSymbolID {
NormalizedSymbolIDllvm::yaml::NormalizedSymbolID65   NormalizedSymbolID(IO &) {}
NormalizedSymbolIDllvm::yaml::NormalizedSymbolID66   NormalizedSymbolID(IO &, const SymbolID &ID) {
67     llvm::raw_string_ostream OS(HexString);
68     OS << ID;
69   }
70 
denormalizellvm::yaml::NormalizedSymbolID71   SymbolID denormalize(IO &I) {
72     auto ID = SymbolID::fromStr(HexString);
73     if (!ID) {
74       I.setError(llvm::toString(ID.takeError()));
75       return SymbolID();
76     }
77     return *ID;
78   }
79 
80   std::string HexString;
81 };
82 
83 struct NormalizedSymbolFlag {
NormalizedSymbolFlagllvm::yaml::NormalizedSymbolFlag84   NormalizedSymbolFlag(IO &) {}
NormalizedSymbolFlagllvm::yaml::NormalizedSymbolFlag85   NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) {
86     Flag = static_cast<uint8_t>(F);
87   }
88 
denormalizellvm::yaml::NormalizedSymbolFlag89   Symbol::SymbolFlag denormalize(IO &) {
90     return static_cast<Symbol::SymbolFlag>(Flag);
91   }
92 
93   uint8_t Flag = 0;
94 };
95 
96 struct NormalizedSymbolOrigin {
NormalizedSymbolOriginllvm::yaml::NormalizedSymbolOrigin97   NormalizedSymbolOrigin(IO &) {}
NormalizedSymbolOriginllvm::yaml::NormalizedSymbolOrigin98   NormalizedSymbolOrigin(IO &, SymbolOrigin O) {
99     Origin = static_cast<uint8_t>(O);
100   }
101 
denormalizellvm::yaml::NormalizedSymbolOrigin102   SymbolOrigin denormalize(IO &) { return static_cast<SymbolOrigin>(Origin); }
103 
104   uint8_t Origin = 0;
105 };
106 
107 template <> struct MappingTraits<YPosition> {
mappingllvm::yaml::MappingTraits108   static void mapping(IO &IO, YPosition &Value) {
109     IO.mapRequired("Line", Value.Line);
110     IO.mapRequired("Column", Value.Column);
111   }
112 };
113 
114 struct NormalizedPosition {
115   using Position = clang::clangd::SymbolLocation::Position;
NormalizedPositionllvm::yaml::NormalizedPosition116   NormalizedPosition(IO &) {}
NormalizedPositionllvm::yaml::NormalizedPosition117   NormalizedPosition(IO &, const Position &Pos) {
118     P.Line = Pos.line();
119     P.Column = Pos.column();
120   }
121 
denormalizellvm::yaml::NormalizedPosition122   Position denormalize(IO &) {
123     Position Pos;
124     Pos.setLine(P.Line);
125     Pos.setColumn(P.Column);
126     return Pos;
127   }
128   YPosition P;
129 };
130 
131 struct NormalizedFileURI {
NormalizedFileURIllvm::yaml::NormalizedFileURI132   NormalizedFileURI(IO &) {}
NormalizedFileURIllvm::yaml::NormalizedFileURI133   NormalizedFileURI(IO &, const char *FileURI) { URI = FileURI; }
134 
denormalizellvm::yaml::NormalizedFileURI135   const char *denormalize(IO &IO) {
136     assert(IO.getContext() &&
137            "Expecting an UniqueStringSaver to allocate data");
138     return static_cast<llvm::UniqueStringSaver *>(IO.getContext())
139         ->save(URI)
140         .data();
141   }
142 
143   std::string URI;
144 };
145 
146 template <> struct MappingTraits<SymbolLocation> {
mappingllvm::yaml::MappingTraits147   static void mapping(IO &IO, SymbolLocation &Value) {
148     MappingNormalization<NormalizedFileURI, const char *> NFile(IO,
149                                                                 Value.FileURI);
150     IO.mapRequired("FileURI", NFile->URI);
151     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NStart(
152         IO, Value.Start);
153     IO.mapRequired("Start", NStart->P);
154     MappingNormalization<NormalizedPosition, SymbolLocation::Position> NEnd(
155         IO, Value.End);
156     IO.mapRequired("End", NEnd->P);
157   }
158 };
159 
160 template <> struct MappingTraits<SymbolInfo> {
mappingllvm::yaml::MappingTraits161   static void mapping(IO &io, SymbolInfo &SymInfo) {
162     // FIXME: expose other fields?
163     io.mapRequired("Kind", SymInfo.Kind);
164     io.mapRequired("Lang", SymInfo.Lang);
165   }
166 };
167 
168 template <>
169 struct MappingTraits<clang::clangd::Symbol::IncludeHeaderWithReferences> {
mappingllvm::yaml::MappingTraits170   static void mapping(IO &io,
171                       clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) {
172     io.mapRequired("Header", Inc.IncludeHeader);
173     io.mapRequired("References", Inc.References);
174   }
175 };
176 
177 template <> struct MappingTraits<Symbol> {
mappingllvm::yaml::MappingTraits178   static void mapping(IO &IO, Symbol &Sym) {
179     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID);
180     MappingNormalization<NormalizedSymbolFlag, Symbol::SymbolFlag> NSymbolFlag(
181         IO, Sym.Flags);
182     MappingNormalization<NormalizedSymbolOrigin, SymbolOrigin> NSymbolOrigin(
183         IO, Sym.Origin);
184     IO.mapRequired("ID", NSymbolID->HexString);
185     IO.mapRequired("Name", Sym.Name);
186     IO.mapRequired("Scope", Sym.Scope);
187     IO.mapRequired("SymInfo", Sym.SymInfo);
188     IO.mapOptional("CanonicalDeclaration", Sym.CanonicalDeclaration,
189                    SymbolLocation());
190     IO.mapOptional("Definition", Sym.Definition, SymbolLocation());
191     IO.mapOptional("References", Sym.References, 0u);
192     IO.mapOptional("Origin", NSymbolOrigin->Origin);
193     IO.mapOptional("Flags", NSymbolFlag->Flag);
194     IO.mapOptional("Signature", Sym.Signature);
195     IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix);
196     IO.mapOptional("Documentation", Sym.Documentation);
197     IO.mapOptional("ReturnType", Sym.ReturnType);
198     IO.mapOptional("Type", Sym.Type);
199     IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders);
200   }
201 };
202 
203 template <> struct ScalarEnumerationTraits<SymbolLanguage> {
enumerationllvm::yaml::ScalarEnumerationTraits204   static void enumeration(IO &IO, SymbolLanguage &Value) {
205     IO.enumCase(Value, "C", SymbolLanguage::C);
206     IO.enumCase(Value, "Cpp", SymbolLanguage::CXX);
207     IO.enumCase(Value, "ObjC", SymbolLanguage::ObjC);
208     IO.enumCase(Value, "Swift", SymbolLanguage::Swift);
209   }
210 };
211 
212 template <> struct ScalarEnumerationTraits<SymbolKind> {
enumerationllvm::yaml::ScalarEnumerationTraits213   static void enumeration(IO &IO, SymbolKind &Value) {
214 #define DEFINE_ENUM(name) IO.enumCase(Value, #name, SymbolKind::name)
215 
216     DEFINE_ENUM(Unknown);
217     DEFINE_ENUM(Function);
218     DEFINE_ENUM(Module);
219     DEFINE_ENUM(Namespace);
220     DEFINE_ENUM(NamespaceAlias);
221     DEFINE_ENUM(Macro);
222     DEFINE_ENUM(Enum);
223     DEFINE_ENUM(Struct);
224     DEFINE_ENUM(Class);
225     DEFINE_ENUM(Protocol);
226     DEFINE_ENUM(Extension);
227     DEFINE_ENUM(Union);
228     DEFINE_ENUM(TypeAlias);
229     DEFINE_ENUM(Function);
230     DEFINE_ENUM(Variable);
231     DEFINE_ENUM(Field);
232     DEFINE_ENUM(EnumConstant);
233     DEFINE_ENUM(InstanceMethod);
234     DEFINE_ENUM(ClassMethod);
235     DEFINE_ENUM(StaticMethod);
236     DEFINE_ENUM(InstanceProperty);
237     DEFINE_ENUM(ClassProperty);
238     DEFINE_ENUM(StaticProperty);
239     DEFINE_ENUM(Constructor);
240     DEFINE_ENUM(Destructor);
241     DEFINE_ENUM(ConversionFunction);
242     DEFINE_ENUM(Parameter);
243     DEFINE_ENUM(Using);
244 
245 #undef DEFINE_ENUM
246   }
247 };
248 
249 template <> struct MappingTraits<RefBundle> {
mappingllvm::yaml::MappingTraits250   static void mapping(IO &IO, RefBundle &Refs) {
251     MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO,
252                                                                  Refs.first);
253     IO.mapRequired("ID", NSymbolID->HexString);
254     IO.mapRequired("References", Refs.second);
255   }
256 };
257 
258 struct NormalizedRefKind {
NormalizedRefKindllvm::yaml::NormalizedRefKind259   NormalizedRefKind(IO &) {}
NormalizedRefKindllvm::yaml::NormalizedRefKind260   NormalizedRefKind(IO &, RefKind O) { Kind = static_cast<uint8_t>(O); }
261 
denormalizellvm::yaml::NormalizedRefKind262   RefKind denormalize(IO &) { return static_cast<RefKind>(Kind); }
263 
264   uint8_t Kind = 0;
265 };
266 
267 template <> struct MappingTraits<Ref> {
mappingllvm::yaml::MappingTraits268   static void mapping(IO &IO, Ref &R) {
269     MappingNormalization<NormalizedRefKind, RefKind> NKind(IO, R.Kind);
270     IO.mapRequired("Kind", NKind->Kind);
271     IO.mapRequired("Location", R.Location);
272   }
273 };
274 
275 template <> struct MappingTraits<VariantEntry> {
mappingllvm::yaml::MappingTraits276   static void mapping(IO &IO, VariantEntry &Variant) {
277     if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) {
278       if (!IO.outputting())
279         Variant.Symbol.emplace();
280       MappingTraits<Symbol>::mapping(IO, *Variant.Symbol);
281     } else if (IO.mapTag("!Refs", Variant.Refs.hasValue())) {
282       if (!IO.outputting())
283         Variant.Refs.emplace();
284       MappingTraits<RefBundle>::mapping(IO, *Variant.Refs);
285     }
286   }
287 };
288 
289 } // namespace yaml
290 } // namespace llvm
291 
292 namespace clang {
293 namespace clangd {
294 
writeYAML(const IndexFileOut & O,llvm::raw_ostream & OS)295 void writeYAML(const IndexFileOut &O, llvm::raw_ostream &OS) {
296   llvm::yaml::Output Yout(OS);
297   for (const auto &Sym : *O.Symbols) {
298     VariantEntry Entry;
299     Entry.Symbol = Sym;
300     Yout << Entry;
301   }
302   if (O.Refs)
303     for (auto &Sym : *O.Refs) {
304       VariantEntry Entry;
305       Entry.Refs = Sym;
306       Yout << Entry;
307     }
308 }
309 
readYAML(llvm::StringRef Data)310 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef Data) {
311   SymbolSlab::Builder Symbols;
312   RefSlab::Builder Refs;
313   llvm::BumpPtrAllocator
314       Arena; // store the underlying data of Position::FileURI.
315   llvm::UniqueStringSaver Strings(Arena);
316   llvm::yaml::Input Yin(Data, &Strings);
317   while (Yin.setCurrentDocument()) {
318     llvm::yaml::EmptyContext Ctx;
319     VariantEntry Variant;
320     yamlize(Yin, Variant, true, Ctx);
321     if (Yin.error())
322       return llvm::errorCodeToError(Yin.error());
323 
324     if (Variant.Symbol)
325       Symbols.insert(*Variant.Symbol);
326     if (Variant.Refs)
327       for (const auto &Ref : Variant.Refs->second)
328         Refs.insert(Variant.Refs->first, Ref);
329     Yin.nextDocument();
330   }
331 
332   IndexFileIn Result;
333   Result.Symbols.emplace(std::move(Symbols).build());
334   Result.Refs.emplace(std::move(Refs).build());
335   return std::move(Result);
336 }
337 
toYAML(const Symbol & S)338 std::string toYAML(const Symbol &S) {
339   std::string Buf;
340   {
341     llvm::raw_string_ostream OS(Buf);
342     llvm::yaml::Output Yout(OS);
343     Symbol Sym = S; // copy: Yout<< requires mutability.
344     Yout << Sym;
345   }
346   return Buf;
347 }
348 
toYAML(const std::pair<SymbolID,llvm::ArrayRef<Ref>> & Data)349 std::string toYAML(const std::pair<SymbolID, llvm::ArrayRef<Ref>> &Data) {
350   RefBundle Refs = {Data.first, Data.second};
351   std::string Buf;
352   {
353     llvm::raw_string_ostream OS(Buf);
354     llvm::yaml::Output Yout(OS);
355     Yout << Refs;
356   }
357   return Buf;
358 }
359 
360 } // namespace clangd
361 } // namespace clang
362