xref: /llvm-project/llvm/tools/llvm-readtapi/DiffEngine.cpp (revision d9a9872ec4760762fdc467ef283cea302a3742e5)
1 //===-- DiffEngine.cpp - Structural file comparison -----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines the implementation of the llvm-tapi difference
10 // engine, which structurally compares two tbd files.
11 //
12 //===----------------------------------------------------------------------===/
13 #include "DiffEngine.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/Support/Casting.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include "llvm/TextAPI/InterfaceFile.h"
18 #include "llvm/TextAPI/Symbol.h"
19 #include "llvm/TextAPI/Target.h"
20 #include <iterator>
21 
22 using namespace llvm;
23 using namespace MachO;
24 using namespace object;
25 
setOrderIndicator(InterfaceInputOrder Order)26 StringRef setOrderIndicator(InterfaceInputOrder Order) {
27   return ((Order == lhs) ? "< " : "> ");
28 }
29 
30 // The following template specialization implementations
31 // need to be explicitly placed into the llvm namespace
32 // to work around a GCC 4.8 bug.
33 namespace llvm {
34 
35 template <typename T, DiffAttrKind U>
print(raw_ostream & OS,std::string Indent)36 inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) {
37   OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n";
38 }
39 
40 template <>
41 inline void
print(raw_ostream & OS,std::string Indent)42 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS,
43                                                     std::string Indent) {
44   OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n";
45 }
46 
47 template <>
48 inline void
print(raw_ostream & OS,std::string Indent)49 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS,
50                                                        std::string Indent) {
51   OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val)
52      << "\n";
53 }
54 
55 template <>
56 inline void
print(raw_ostream & OS,std::string Indent)57 DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS,
58                                                 std::string Indent) {
59   OS << Indent << "\t" << setOrderIndicator(Order)
60      << ((Val == true) ? "true" : "false") << "\n";
61 }
62 
63 } // end namespace llvm
64 
getSymbolNamePrefix(MachO::EncodeKind Kind)65 StringLiteral SymScalar::getSymbolNamePrefix(MachO::EncodeKind Kind) {
66   switch (Kind) {
67   case MachO::EncodeKind::GlobalSymbol:
68     return StringLiteral("");
69   case MachO::EncodeKind::ObjectiveCClass:
70     return ObjC2MetaClassNamePrefix;
71   case MachO::EncodeKind ::ObjectiveCClassEHType:
72     return ObjC2EHTypePrefix;
73   case MachO::EncodeKind ::ObjectiveCInstanceVariable:
74     return ObjC2IVarPrefix;
75   }
76   llvm_unreachable("Unknown llvm::MachO::EncodeKind enum");
77 }
78 
getFlagString(const MachO::Symbol * Sym)79 std::string SymScalar::getFlagString(const MachO::Symbol *Sym) {
80   if (Sym->getFlags() == SymbolFlags::None)
81     return {};
82   SmallString<64> Flags(" - ");
83   if (Sym->isThreadLocalValue())
84     Flags.append("Thread-Local ");
85   if (Sym->isWeakDefined())
86     Flags.append("Weak-Defined ");
87   if (Sym->isWeakReferenced())
88     Flags.append("Weak-Referenced ");
89   if (Sym->isUndefined())
90     Flags.append("Undefined ");
91   if (Sym->isReexported())
92     Flags.append("Reexported ");
93   if (Sym->isData())
94     Flags.append("Data ");
95   if (Sym->isText())
96     Flags.append("Text ");
97 
98   return std::string(Flags);
99 }
100 
print(raw_ostream & OS,std::string Indent,MachO::Target Targ)101 void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) {
102   if (Val->getKind() == MachO::EncodeKind::ObjectiveCClass) {
103     if (Targ.Arch == MachO::AK_i386 && Targ.Platform == MachO::PLATFORM_MACOS) {
104       OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
105          << ObjC1ClassNamePrefix << Val->getName() << getFlagString(Val)
106          << "\n";
107       return;
108     }
109     OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
110        << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Val) << "\n";
111   }
112   OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ")
113      << getSymbolNamePrefix(Val->getKind()) << Val->getName()
114      << getFlagString(Val) << "\n";
115 }
116 
checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS,llvm::MachO::InterfaceFile::const_symbol_range RHS)117 bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS,
118                          llvm::MachO::InterfaceFile::const_symbol_range RHS) {
119   if (std::distance(LHS.begin(), LHS.end()) !=
120       std::distance(RHS.begin(), RHS.end()))
121     return false;
122   return std::equal(LHS.begin(), LHS.end(), RHS.begin(),
123                     [&](auto LHS, auto RHS) { return *LHS == *RHS; });
124 }
125 
126 template <typename TargetVecT, typename ValTypeT, typename V>
addDiffForTargSlice(V Val,Target Targ,DiffOutput & Diff,InterfaceInputOrder Order)127 void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff,
128                          InterfaceInputOrder Order) {
129   auto TargetVector = llvm::find_if(
130       Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) {
131         if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get()))
132           return TVec->Targ == Targ;
133         return false;
134       });
135   if (TargetVector != Diff.Values.end()) {
136     ValTypeT NewVal(Order, Val);
137     cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal);
138   } else {
139     auto NewTargetVec = std::make_unique<TargetVecT>(Targ);
140     ValTypeT NewVal(Order, Val);
141     NewTargetVec->TargValues.push_back(NewVal);
142     Diff.Values.push_back(std::move(NewTargetVec));
143   }
144 }
145 
getSingleAttrDiff(const std::vector<InterfaceFileRef> & IRefVec,std::string Name,InterfaceInputOrder Order)146 DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec,
147                              std::string Name, InterfaceInputOrder Order) {
148   DiffOutput Diff(Name);
149   Diff.Kind = AD_Str_Vec;
150   for (const auto &IRef : IRefVec)
151     for (auto Targ : IRef.targets())
152       addDiffForTargSlice<DiffStrVec,
153                           DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
154           IRef.getInstallName(), Targ, Diff, Order);
155   return Diff;
156 }
157 
158 DiffOutput
getSingleAttrDiff(const std::vector<std::pair<Target,std::string>> & PairVec,std::string Name,InterfaceInputOrder Order)159 getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec,
160                   std::string Name, InterfaceInputOrder Order) {
161   DiffOutput Diff(Name);
162   Diff.Kind = AD_Str_Vec;
163   for (const auto &Pair : PairVec)
164     addDiffForTargSlice<DiffStrVec,
165                         DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
166         StringRef(Pair.second), Pair.first, Diff, Order);
167   return Diff;
168 }
169 
getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange,std::string Name,InterfaceInputOrder Order)170 DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange,
171                              std::string Name, InterfaceInputOrder Order) {
172   DiffOutput Diff(Name);
173   Diff.Kind = AD_Sym_Vec;
174   for (const auto *Sym : SymRange)
175     for (auto Targ : Sym->targets())
176       addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order);
177   return Diff;
178 }
179 
180 template <typename T>
getSingleAttrDiff(T SingleAttr,std::string Attribute)181 DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) {
182   DiffOutput Diff(Attribute);
183   Diff.Kind = SingleAttr.getKind();
184   Diff.Values.push_back(std::make_unique<T>(SingleAttr));
185   return Diff;
186 }
187 
188 template <typename T, DiffAttrKind U>
diffAttribute(std::string Name,std::vector<DiffOutput> & Output,DiffScalarVal<T,U> Attr)189 void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
190                    DiffScalarVal<T, U> Attr) {
191   Output.push_back(getSingleAttrDiff(Attr, Name));
192 }
193 
194 template <typename T>
diffAttribute(std::string Name,std::vector<DiffOutput> & Output,const T & Val,InterfaceInputOrder Order)195 void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,
196                    const T &Val, InterfaceInputOrder Order) {
197   Output.push_back(getSingleAttrDiff(Val, Name, Order));
198 }
199 
getSingleIF(InterfaceFile * Interface,InterfaceInputOrder Order)200 std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface,
201                                     InterfaceInputOrder Order) {
202   std::vector<DiffOutput> Output;
203   diffAttribute("Install Name", Output,
204                 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(
205                     Order, Interface->getInstallName()));
206   diffAttribute("Current Version", Output,
207                 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
208                     Order, Interface->getCurrentVersion()));
209   diffAttribute("Compatibility Version", Output,
210                 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
211                     Order, Interface->getCompatibilityVersion()));
212   diffAttribute("Swift ABI Version", Output,
213                 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
214                     Order, Interface->getSwiftABIVersion()));
215   diffAttribute("Two Level Namespace", Output,
216                 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
217                     Order, Interface->isTwoLevelNamespace()));
218   diffAttribute("Application Extension Safe", Output,
219                 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
220                     Order, Interface->isApplicationExtensionSafe()));
221   diffAttribute("Reexported Libraries", Output,
222                 Interface->reexportedLibraries(), Order);
223   diffAttribute("Allowable Clients", Output, Interface->allowableClients(),
224                 Order);
225   diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order);
226   diffAttribute("Symbols", Output, Interface->symbols(), Order);
227   for (const auto &Doc : Interface->documents()) {
228     DiffOutput Documents("Inlined Reexported Frameworks/Libraries");
229     Documents.Kind = AD_Inline_Doc;
230     Documents.Values.push_back(std::make_unique<InlineDoc>(
231         InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order))));
232     Output.push_back(std::move(Documents));
233   }
234   return Output;
235 }
236 
findAndAddDiff(const std::vector<InterfaceFileRef> & CollectedIRefVec,const std::vector<InterfaceFileRef> & LookupIRefVec,DiffOutput & Result,InterfaceInputOrder Order)237 void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec,
238                     const std::vector<InterfaceFileRef> &LookupIRefVec,
239                     DiffOutput &Result, InterfaceInputOrder Order) {
240   Result.Kind = AD_Str_Vec;
241   for (const auto &IRef : CollectedIRefVec)
242     for (auto Targ : IRef.targets()) {
243       auto FoundIRef = llvm::any_of(LookupIRefVec, [&](const auto LIRef) {
244         return llvm::is_contained(LIRef.targets(), Targ) &&
245                IRef.getInstallName() == LIRef.getInstallName();
246       });
247       if (!FoundIRef)
248         addDiffForTargSlice<DiffStrVec,
249                             DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
250             IRef.getInstallName(), Targ, Result, Order);
251     }
252 }
253 
findAndAddDiff(const std::vector<std::pair<Target,std::string>> & CollectedPairs,const std::vector<std::pair<Target,std::string>> & LookupPairs,DiffOutput & Result,InterfaceInputOrder Order)254 void findAndAddDiff(
255     const std::vector<std::pair<Target, std::string>> &CollectedPairs,
256     const std::vector<std::pair<Target, std::string>> &LookupPairs,
257     DiffOutput &Result, InterfaceInputOrder Order) {
258   Result.Kind = AD_Str_Vec;
259   for (const auto &Pair : CollectedPairs) {
260     auto FoundPair = llvm::find(LookupPairs, Pair);
261     if (FoundPair == LookupPairs.end())
262       addDiffForTargSlice<DiffStrVec,
263                           DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
264           StringRef(Pair.second), Pair.first, Result, Order);
265   }
266 }
267 
findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms,InterfaceFile::const_symbol_range LookupSyms,DiffOutput & Result,InterfaceInputOrder Order)268 void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms,
269                     InterfaceFile::const_symbol_range LookupSyms,
270                     DiffOutput &Result, InterfaceInputOrder Order) {
271   Result.Kind = AD_Sym_Vec;
272   for (const auto *Sym : CollectedSyms)
273     for (const auto Targ : Sym->targets()) {
274       auto FoundSym = llvm::any_of(LookupSyms, [&](const auto LSym) {
275         return (Sym->getName() == LSym->getName() &&
276                 Sym->getKind() == LSym->getKind() &&
277                 Sym->getFlags() == LSym->getFlags() &&
278                 llvm::is_contained(LSym->targets(), Targ));
279       });
280       if (!FoundSym)
281         addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order);
282     }
283 }
284 
285 template <typename T>
recordDifferences(T LHS,T RHS,std::string Attr)286 DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) {
287   DiffOutput Diff(Attr);
288   if (LHS.getKind() == RHS.getKind()) {
289     Diff.Kind = LHS.getKind();
290     Diff.Values.push_back(std::make_unique<T>(LHS));
291     Diff.Values.push_back(std::make_unique<T>(RHS));
292   }
293   return Diff;
294 }
295 
296 template <typename T>
recordDifferences(const std::vector<T> & LHS,const std::vector<T> & RHS,std::string Attr)297 DiffOutput recordDifferences(const std::vector<T> &LHS,
298                              const std::vector<T> &RHS, std::string Attr) {
299   DiffOutput Diff(Attr);
300   Diff.Kind = AD_Str_Vec;
301   findAndAddDiff(LHS, RHS, Diff, lhs);
302   findAndAddDiff(RHS, LHS, Diff, rhs);
303   return Diff;
304 }
305 
recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS,llvm::MachO::InterfaceFile::const_symbol_range RHS,std::string Attr)306 DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS,
307                              llvm::MachO::InterfaceFile::const_symbol_range RHS,
308                              std::string Attr) {
309   DiffOutput Diff(Attr);
310   Diff.Kind = AD_Sym_Vec;
311   findAndAddDiff(LHS, RHS, Diff, lhs);
312   findAndAddDiff(RHS, LHS, Diff, rhs);
313   return Diff;
314 }
315 
316 std::vector<DiffOutput>
findDifferences(const InterfaceFile * IFLHS,const InterfaceFile * IFRHS)317 DiffEngine::findDifferences(const InterfaceFile *IFLHS,
318                             const InterfaceFile *IFRHS) {
319   std::vector<DiffOutput> Output;
320   if (IFLHS->getInstallName() != IFRHS->getInstallName())
321     Output.push_back(recordDifferences(
322         DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs,
323                                                      IFLHS->getInstallName()),
324         DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs,
325                                                      IFRHS->getInstallName()),
326         "Install Name"));
327 
328   if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion())
329     Output.push_back(recordDifferences(
330         DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
331             lhs, IFLHS->getCurrentVersion()),
332         DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
333             rhs, IFRHS->getCurrentVersion()),
334         "Current Version"));
335   if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion())
336     Output.push_back(recordDifferences(
337         DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
338             lhs, IFLHS->getCompatibilityVersion()),
339         DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(
340             rhs, IFRHS->getCompatibilityVersion()),
341         "Compatibility Version"));
342   if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion())
343     Output.push_back(
344         recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
345                               lhs, IFLHS->getSwiftABIVersion()),
346                           DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(
347                               rhs, IFRHS->getSwiftABIVersion()),
348                           "Swift ABI Version"));
349 
350   if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace())
351     Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
352                                            lhs, IFLHS->isTwoLevelNamespace()),
353                                        DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
354                                            rhs, IFRHS->isTwoLevelNamespace()),
355                                        "Two Level Namespace"));
356 
357   if (IFLHS->isApplicationExtensionSafe() !=
358       IFRHS->isApplicationExtensionSafe())
359     Output.push_back(
360         recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
361                               lhs, IFLHS->isApplicationExtensionSafe()),
362                           DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
363                               rhs, IFRHS->isApplicationExtensionSafe()),
364                           "Application Extension Safe"));
365 
366   if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport())
367     Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
368                                            lhs, IFLHS->hasSimulatorSupport()),
369                                        DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
370                                            rhs, IFRHS->hasSimulatorSupport()),
371                                        "Simulator Support"));
372 
373   if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache())
374     Output.push_back(
375         recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
376                               lhs, IFLHS->isOSLibNotForSharedCache()),
377                           DiffScalarVal<bool, AD_Diff_Scalar_Bool>(
378                               rhs, IFRHS->isOSLibNotForSharedCache()),
379                           "Shared Cache Ineligible"));
380 
381   if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries())
382     Output.push_back(recordDifferences(IFLHS->reexportedLibraries(),
383                                        IFRHS->reexportedLibraries(),
384                                        "Reexported Libraries"));
385 
386   if (IFLHS->rpaths() != IFRHS->rpaths())
387     Output.push_back(recordDifferences(IFLHS->rpaths(), IFRHS->rpaths(),
388                                        "Run Path Search Paths"));
389 
390   if (IFLHS->allowableClients() != IFRHS->allowableClients())
391     Output.push_back(recordDifferences(IFLHS->allowableClients(),
392                                        IFRHS->allowableClients(),
393                                        "Allowable Clients"));
394 
395   if (IFLHS->umbrellas() != IFRHS->umbrellas())
396     Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(),
397                                        "Parent Umbrellas"));
398 
399   if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols()))
400     Output.push_back(
401         recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols"));
402 
403   if (IFLHS->documents() != IFRHS->documents()) {
404     DiffOutput Docs("Inlined Reexported Frameworks/Libraries");
405     Docs.Kind = AD_Inline_Doc;
406     std::vector<StringRef> DocsInserted;
407     // Iterate through inline frameworks/libraries from interface file and find
408     // match based on install name.
409     for (auto DocLHS : IFLHS->documents()) {
410       auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) {
411         return (DocLHS->getInstallName() == DocRHS->getInstallName());
412       });
413       // If a match found, recursively get differences between the pair.
414       if (Pair != IFRHS->documents().end()) {
415         InlineDoc PairDiff =
416             InlineDoc(DocLHS->getInstallName(),
417                       findDifferences(DocLHS.get(), Pair->get()));
418         if (!PairDiff.DocValues.empty())
419           Docs.Values.push_back(
420               std::make_unique<InlineDoc>(std::move(PairDiff)));
421       }
422       // If a match is not found, get attributes from single item.
423       else
424         Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
425             DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs))));
426       DocsInserted.push_back(DocLHS->getInstallName());
427     }
428     for (auto DocRHS : IFRHS->documents()) {
429       auto WasGathered =
430           llvm::any_of(DocsInserted, [&](const auto &GatheredDoc) {
431             return (GatheredDoc == DocRHS->getInstallName());
432           });
433       if (!WasGathered)
434         Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc(
435             DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs))));
436     }
437     if (!Docs.Values.empty())
438       Output.push_back(std::move(Docs));
439   }
440   return Output;
441 }
442 
443 template <typename T>
printSingleVal(std::string Indent,const DiffOutput & Attr,raw_ostream & OS)444 void printSingleVal(std::string Indent, const DiffOutput &Attr,
445                     raw_ostream &OS) {
446   if (Attr.Values.empty())
447     return;
448   OS << Indent << Attr.Name << "\n";
449   for (auto &RawItem : Attr.Values)
450     if (T *Item = dyn_cast<T>(RawItem.get()))
451       Item->print(OS, Indent);
452 }
453 
454 template <typename T>
castValues(const std::unique_ptr<AttributeDiff> & RawAttr)455 T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) {
456   T *CastAttr = cast<T>(RawAttr.get());
457   return CastAttr;
458 }
459 
sortTargetValues(std::vector<T> & TargValues)460 template <typename T> void sortTargetValues(std::vector<T> &TargValues) {
461   llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {
462     if (ValA.getOrder() == ValB.getOrder()) {
463       return ValA.getVal() < ValB.getVal();
464     }
465     return ValA.getOrder() < ValB.getOrder();
466   });
467 }
468 
469 template <typename T>
printVecVal(std::string Indent,const DiffOutput & Attr,raw_ostream & OS)470 void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) {
471   if (Attr.Values.empty())
472     return;
473 
474   OS << Indent << Attr.Name << "\n";
475 
476   std::vector<T *> SortedAttrs;
477 
478   llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>);
479 
480   llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
481     return ValA->Targ < ValB->Targ;
482   });
483 
484   for (auto *Vec : SortedAttrs) {
485     sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(
486         Vec->TargValues);
487     OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n";
488     for (auto &Item : Vec->TargValues)
489       Item.print(OS, Indent);
490   }
491 }
492 
493 template <>
printVecVal(std::string Indent,const DiffOutput & Attr,raw_ostream & OS)494 void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr,
495                              raw_ostream &OS) {
496   if (Attr.Values.empty())
497     return;
498 
499   OS << Indent << Attr.Name << "\n";
500 
501   std::vector<DiffSymVec *> SortedAttrs;
502 
503   llvm::transform(Attr.Values, std::back_inserter(SortedAttrs),
504                   castValues<DiffSymVec>);
505 
506   llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {
507     return ValA->Targ < ValB->Targ;
508   });
509   for (auto *SymVec : SortedAttrs) {
510     sortTargetValues<SymScalar>(SymVec->TargValues);
511     OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n";
512     for (auto &Item : SymVec->TargValues)
513       Item.print(OS, Indent, SymVec->Targ);
514   }
515 }
516 
printDifferences(raw_ostream & OS,const std::vector<DiffOutput> & Diffs,int IndentCounter)517 void DiffEngine::printDifferences(raw_ostream &OS,
518                                   const std::vector<DiffOutput> &Diffs,
519                                   int IndentCounter) {
520   std::string Indent = std::string(IndentCounter, '\t');
521   for (auto &Attr : Diffs) {
522     switch (Attr.Kind) {
523     case AD_Diff_Scalar_Str:
524       if (IndentCounter == 0)
525         printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent,
526                                                                      Attr, OS);
527       break;
528     case AD_Diff_Scalar_PackedVersion:
529       printSingleVal<
530           DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent,
531                                                                       Attr, OS);
532       break;
533     case AD_Diff_Scalar_Unsigned:
534       printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent,
535                                                                       Attr, OS);
536       break;
537     case AD_Diff_Scalar_Bool:
538       printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr,
539                                                                OS);
540       break;
541     case AD_Str_Vec:
542       printVecVal<DiffStrVec>(Indent, Attr, OS);
543       break;
544     case AD_Sym_Vec:
545       printVecVal<DiffSymVec>(Indent, Attr, OS);
546       break;
547     case AD_Inline_Doc:
548       if (!Attr.Values.empty()) {
549         OS << Indent << Attr.Name << "\n";
550         for (auto &Item : Attr.Values)
551           if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get()))
552             if (!Doc->DocValues.empty()) {
553               OS << Indent << "\t" << Doc->InstallName << "\n";
554               printDifferences(OS, std::move(Doc->DocValues), 2);
555             }
556       }
557       break;
558     }
559   }
560 }
561 
compareFiles(raw_ostream & OS)562 bool DiffEngine::compareFiles(raw_ostream &OS) {
563   if (*FileLHS == *FileRHS)
564     return false;
565   OS << "< " << std::string(FileLHS->getPath().data()) << "\n> "
566      << std::string(FileRHS->getPath().data()) << "\n\n";
567   std::vector<DiffOutput> Diffs = findDifferences(FileLHS, FileRHS);
568   printDifferences(OS, Diffs, 0);
569   return true;
570 }
571