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 | |
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> |
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 |
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 |
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: Val) |
52 | << "\n" ; |
53 | } |
54 | |
55 | template <> |
56 | inline void |
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 | |
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 | |
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(RHS: "Thread-Local " ); |
85 | if (Sym->isWeakDefined()) |
86 | Flags.append(RHS: "Weak-Defined " ); |
87 | if (Sym->isWeakReferenced()) |
88 | Flags.append(RHS: "Weak-Referenced " ); |
89 | if (Sym->isUndefined()) |
90 | Flags.append(RHS: "Undefined " ); |
91 | if (Sym->isReexported()) |
92 | Flags.append(RHS: "Reexported " ); |
93 | if (Sym->isData()) |
94 | Flags.append(RHS: "Data " ); |
95 | if (Sym->isText()) |
96 | Flags.append(RHS: "Text " ); |
97 | |
98 | return std::string(Flags); |
99 | } |
100 | |
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(Sym: Val) |
106 | << "\n" ; |
107 | return; |
108 | } |
109 | OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> " ) |
110 | << ObjC2ClassNamePrefix << Val->getName() << getFlagString(Sym: Val) << "\n" ; |
111 | } |
112 | OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> " ) |
113 | << getSymbolNamePrefix(Kind: Val->getKind()) << Val->getName() |
114 | << getFlagString(Sym: Val) << "\n" ; |
115 | } |
116 | |
117 | bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, |
118 | llvm::MachO::InterfaceFile::const_symbol_range RHS) { |
119 | if (std::distance(first: LHS.begin(), last: LHS.end()) != |
120 | std::distance(first: RHS.begin(), last: 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> |
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 | |
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 | Val: IRef.getInstallName(), Targ, Diff, Order); |
155 | return Diff; |
156 | } |
157 | |
158 | DiffOutput |
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 | Val: StringRef(Pair.second), Targ: Pair.first, Diff, Order); |
167 | return Diff; |
168 | } |
169 | |
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>(Val: Sym, Targ, Diff, Order); |
177 | return Diff; |
178 | } |
179 | |
180 | template <typename T> |
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> |
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> |
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 | |
200 | std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, |
201 | InterfaceInputOrder Order) { |
202 | std::vector<DiffOutput> Output; |
203 | diffAttribute(Name: "Install Name" , Output, |
204 | Attr: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( |
205 | Order, Interface->getInstallName())); |
206 | diffAttribute(Name: "Current Version" , Output, |
207 | Attr: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
208 | Order, Interface->getCurrentVersion())); |
209 | diffAttribute(Name: "Compatibility Version" , Output, |
210 | Attr: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
211 | Order, Interface->getCompatibilityVersion())); |
212 | diffAttribute(Name: "Swift ABI Version" , Output, |
213 | Attr: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( |
214 | Order, Interface->getSwiftABIVersion())); |
215 | diffAttribute(Name: "Two Level Namespace" , Output, |
216 | Attr: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
217 | Order, Interface->isTwoLevelNamespace())); |
218 | diffAttribute(Name: "Application Extension Safe" , Output, |
219 | Attr: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
220 | Order, Interface->isApplicationExtensionSafe())); |
221 | diffAttribute(Name: "Reexported Libraries" , Output, |
222 | Val: Interface->reexportedLibraries(), Order); |
223 | diffAttribute(Name: "Allowable Clients" , Output, Val: Interface->allowableClients(), |
224 | Order); |
225 | diffAttribute(Name: "Parent Umbrellas" , Output, Val: Interface->umbrellas(), Order); |
226 | diffAttribute(Name: "Symbols" , Output, Val: 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(x: std::make_unique<InlineDoc>( |
231 | args: InlineDoc(Doc->getInstallName(), getSingleIF(Interface: Doc.get(), Order)))); |
232 | Output.push_back(x: std::move(Documents)); |
233 | } |
234 | return Output; |
235 | } |
236 | |
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(Range: LookupIRefVec, P: [&](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 | Val: IRef.getInstallName(), Targ, Diff&: Result, Order); |
251 | } |
252 | } |
253 | |
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(Range: LookupPairs, Val: Pair); |
261 | if (FoundPair == LookupPairs.end()) |
262 | addDiffForTargSlice<DiffStrVec, |
263 | DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( |
264 | Val: StringRef(Pair.second), Targ: Pair.first, Diff&: Result, Order); |
265 | } |
266 | } |
267 | |
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(Range&: LookupSyms, P: [&](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>(Val: Sym, Targ, Diff&: Result, Order); |
282 | } |
283 | } |
284 | |
285 | template <typename T> |
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> |
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 | |
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(CollectedSyms: LHS, LookupSyms: RHS, Result&: Diff, Order: lhs); |
312 | findAndAddDiff(CollectedSyms: RHS, LookupSyms: LHS, Result&: Diff, Order: rhs); |
313 | return Diff; |
314 | } |
315 | |
316 | std::vector<DiffOutput> |
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(x: recordDifferences( |
322 | LHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, |
323 | IFLHS->getInstallName()), |
324 | RHS: DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, |
325 | IFRHS->getInstallName()), |
326 | Attr: "Install Name" )); |
327 | |
328 | if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) |
329 | Output.push_back(x: recordDifferences( |
330 | LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
331 | lhs, IFLHS->getCurrentVersion()), |
332 | RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
333 | rhs, IFRHS->getCurrentVersion()), |
334 | Attr: "Current Version" )); |
335 | if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) |
336 | Output.push_back(x: recordDifferences( |
337 | LHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
338 | lhs, IFLHS->getCompatibilityVersion()), |
339 | RHS: DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( |
340 | rhs, IFRHS->getCompatibilityVersion()), |
341 | Attr: "Compatibility Version" )); |
342 | if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) |
343 | Output.push_back( |
344 | x: recordDifferences(LHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( |
345 | lhs, IFLHS->getSwiftABIVersion()), |
346 | RHS: DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( |
347 | rhs, IFRHS->getSwiftABIVersion()), |
348 | Attr: "Swift ABI Version" )); |
349 | |
350 | if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) |
351 | Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
352 | lhs, IFLHS->isTwoLevelNamespace()), |
353 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
354 | rhs, IFRHS->isTwoLevelNamespace()), |
355 | Attr: "Two Level Namespace" )); |
356 | |
357 | if (IFLHS->isApplicationExtensionSafe() != |
358 | IFRHS->isApplicationExtensionSafe()) |
359 | Output.push_back( |
360 | x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
361 | lhs, IFLHS->isApplicationExtensionSafe()), |
362 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
363 | rhs, IFRHS->isApplicationExtensionSafe()), |
364 | Attr: "Application Extension Safe" )); |
365 | |
366 | if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport()) |
367 | Output.push_back(x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
368 | lhs, IFLHS->hasSimulatorSupport()), |
369 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
370 | rhs, IFRHS->hasSimulatorSupport()), |
371 | Attr: "Simulator Support" )); |
372 | |
373 | if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache()) |
374 | Output.push_back( |
375 | x: recordDifferences(LHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
376 | lhs, IFLHS->isOSLibNotForSharedCache()), |
377 | RHS: DiffScalarVal<bool, AD_Diff_Scalar_Bool>( |
378 | rhs, IFRHS->isOSLibNotForSharedCache()), |
379 | Attr: "Shared Cache Ineligible" )); |
380 | |
381 | if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) |
382 | Output.push_back(x: recordDifferences(LHS: IFLHS->reexportedLibraries(), |
383 | RHS: IFRHS->reexportedLibraries(), |
384 | Attr: "Reexported Libraries" )); |
385 | |
386 | if (IFLHS->rpaths() != IFRHS->rpaths()) |
387 | Output.push_back(x: recordDifferences(LHS: IFLHS->rpaths(), RHS: IFRHS->rpaths(), |
388 | Attr: "Run Path Search Paths" )); |
389 | |
390 | if (IFLHS->allowableClients() != IFRHS->allowableClients()) |
391 | Output.push_back(x: recordDifferences(LHS: IFLHS->allowableClients(), |
392 | RHS: IFRHS->allowableClients(), |
393 | Attr: "Allowable Clients" )); |
394 | |
395 | if (IFLHS->umbrellas() != IFRHS->umbrellas()) |
396 | Output.push_back(x: recordDifferences(LHS: IFLHS->umbrellas(), RHS: IFRHS->umbrellas(), |
397 | Attr: "Parent Umbrellas" )); |
398 | |
399 | if (!checkSymbolEquality(LHS: IFLHS->symbols(), RHS: IFRHS->symbols())) |
400 | Output.push_back( |
401 | x: recordDifferences(LHS: IFLHS->symbols(), RHS: IFRHS->symbols(), Attr: "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(Range: IFRHS->documents(), P: [&](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(IFLHS: DocLHS.get(), IFRHS: Pair->get())); |
418 | if (!PairDiff.DocValues.empty()) |
419 | Docs.Values.push_back( |
420 | x: std::make_unique<InlineDoc>(args: std::move(PairDiff))); |
421 | } |
422 | // If a match is not found, get attributes from single item. |
423 | else |
424 | Docs.Values.push_back(x: std::make_unique<InlineDoc>(args: InlineDoc( |
425 | DocLHS->getInstallName(), getSingleIF(Interface: DocLHS.get(), Order: lhs)))); |
426 | DocsInserted.push_back(x: DocLHS->getInstallName()); |
427 | } |
428 | for (auto DocRHS : IFRHS->documents()) { |
429 | auto WasGathered = |
430 | llvm::any_of(Range&: DocsInserted, P: [&](const auto &GatheredDoc) { |
431 | return (GatheredDoc == DocRHS->getInstallName()); |
432 | }); |
433 | if (!WasGathered) |
434 | Docs.Values.push_back(x: std::make_unique<InlineDoc>(args: InlineDoc( |
435 | DocRHS->getInstallName(), getSingleIF(Interface: DocRHS.get(), Order: rhs)))); |
436 | } |
437 | if (!Docs.Values.empty()) |
438 | Output.push_back(x: std::move(Docs)); |
439 | } |
440 | return Output; |
441 | } |
442 | |
443 | template <typename T> |
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> |
455 | T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { |
456 | T *CastAttr = cast<T>(RawAttr.get()); |
457 | return CastAttr; |
458 | } |
459 | |
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> |
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 <> |
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(Range: Attr.Values, d_first: std::back_inserter(x&: SortedAttrs), |
504 | F: castValues<DiffSymVec>); |
505 | |
506 | llvm::sort(C&: SortedAttrs, Comp: [&](const auto &ValA, const auto &ValB) { |
507 | return ValA->Targ < ValB->Targ; |
508 | }); |
509 | for (auto *SymVec : SortedAttrs) { |
510 | sortTargetValues<SymScalar>(TargValues&: SymVec->TargValues); |
511 | OS << Indent << "\t" << getTargetTripleName(Targ: SymVec->Targ) << "\n" ; |
512 | for (auto &Item : SymVec->TargValues) |
513 | Item.print(OS, Indent, Targ: SymVec->Targ); |
514 | } |
515 | } |
516 | |
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>(Val: Item.get())) |
552 | if (!Doc->DocValues.empty()) { |
553 | OS << Indent << "\t" << Doc->InstallName << "\n" ; |
554 | printDifferences(OS, Diffs: std::move(Doc->DocValues), IndentCounter: 2); |
555 | } |
556 | } |
557 | break; |
558 | } |
559 | } |
560 | } |
561 | |
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(IFLHS: FileLHS, IFRHS: FileRHS); |
568 | printDifferences(OS, Diffs, IndentCounter: 0); |
569 | return true; |
570 | } |
571 | |