565 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the implementation of the llvm-tapi difference10// 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 22using namespace llvm;23using namespace MachO;24using namespace object;25 26StringRef setOrderIndicator(InterfaceInputOrder Order) {27 return ((Order == lhs) ? "< " : "> ");28}29 30// The following template specialization implementations31// need to be explicitly placed into the llvm namespace32// to work around a GCC 4.8 bug.33namespace llvm {34 35template <typename T, DiffAttrKind U>36inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) {37 OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n";38}39 40template <>41inline void42DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS,43 std::string Indent) {44 OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n";45}46 47template <>48inline void49DiffScalarVal<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 55template <>56inline void57DiffScalarVal<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 llvm64 65StringLiteral 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 79std::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 101void 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 117bool 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 126template <typename TargetVecT, typename ValTypeT, typename V>127void 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 146DiffOutput 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 158DiffOutput159getSingleAttrDiff(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 170DiffOutput 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 180template <typename T>181DiffOutput 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 188template <typename T, DiffAttrKind U>189void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,190 DiffScalarVal<T, U> Attr) {191 Output.push_back(getSingleAttrDiff(Attr, Name));192}193 194template <typename T>195void diffAttribute(std::string Name, std::vector<DiffOutput> &Output,196 const T &Val, InterfaceInputOrder Order) {197 Output.push_back(getSingleAttrDiff(Val, Name, Order));198}199 200std::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 for (const auto &Doc : Interface->documents()) {207 DiffOutput Documents("Inlined Reexported Frameworks/Libraries");208 Documents.Kind = AD_Inline_Doc;209 Documents.Values.push_back(std::make_unique<InlineDoc>(210 InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order))));211 Output.push_back(std::move(Documents));212 }213 return Output;214}215 216void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec,217 const std::vector<InterfaceFileRef> &LookupIRefVec,218 DiffOutput &Result, InterfaceInputOrder Order) {219 Result.Kind = AD_Str_Vec;220 for (const auto &IRef : CollectedIRefVec)221 for (auto Targ : IRef.targets()) {222 auto FoundIRef = llvm::any_of(LookupIRefVec, [&](const auto LIRef) {223 return llvm::is_contained(LIRef.targets(), Targ) &&224 IRef.getInstallName() == LIRef.getInstallName();225 });226 if (!FoundIRef)227 addDiffForTargSlice<DiffStrVec,228 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(229 IRef.getInstallName(), Targ, Result, Order);230 }231}232 233void findAndAddDiff(234 const std::vector<std::pair<Target, std::string>> &CollectedPairs,235 const std::vector<std::pair<Target, std::string>> &LookupPairs,236 DiffOutput &Result, InterfaceInputOrder Order) {237 Result.Kind = AD_Str_Vec;238 for (const auto &Pair : CollectedPairs) {239 auto FoundPair = llvm::find(LookupPairs, Pair);240 if (FoundPair == LookupPairs.end())241 addDiffForTargSlice<DiffStrVec,242 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(243 StringRef(Pair.second), Pair.first, Result, Order);244 }245}246 247void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms,248 InterfaceFile::const_symbol_range LookupSyms,249 DiffOutput &Result, InterfaceInputOrder Order) {250 Result.Kind = AD_Sym_Vec;251 for (const auto *Sym : CollectedSyms)252 for (const auto Targ : Sym->targets()) {253 auto FoundSym = llvm::any_of(LookupSyms, [&](const auto LSym) {254 return (Sym->getName() == LSym->getName() &&255 Sym->getKind() == LSym->getKind() &&256 Sym->getFlags() == LSym->getFlags() &&257 llvm::is_contained(LSym->targets(), Targ));258 });259 if (!FoundSym)260 addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order);261 }262}263 264template <typename T>265DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) {266 DiffOutput Diff(Attr);267 if (LHS.getKind() == RHS.getKind()) {268 Diff.Kind = LHS.getKind();269 Diff.Values.push_back(std::make_unique<T>(LHS));270 Diff.Values.push_back(std::make_unique<T>(RHS));271 }272 return Diff;273}274 275template <typename T>276DiffOutput recordDifferences(const std::vector<T> &LHS,277 const std::vector<T> &RHS, std::string Attr) {278 DiffOutput Diff(Attr);279 Diff.Kind = AD_Str_Vec;280 findAndAddDiff(LHS, RHS, Diff, lhs);281 findAndAddDiff(RHS, LHS, Diff, rhs);282 return Diff;283}284 285DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS,286 llvm::MachO::InterfaceFile::const_symbol_range RHS,287 std::string Attr) {288 DiffOutput Diff(Attr);289 Diff.Kind = AD_Sym_Vec;290 findAndAddDiff(LHS, RHS, Diff, lhs);291 findAndAddDiff(RHS, LHS, Diff, rhs);292 return Diff;293}294 295std::vector<DiffOutput>296DiffEngine::findDifferences(const InterfaceFile *IFLHS,297 const InterfaceFile *IFRHS) {298 std::vector<DiffOutput> Output;299 if (IFLHS->getInstallName() != IFRHS->getInstallName())300 Output.push_back(recordDifferences(301 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs,302 IFLHS->getInstallName()),303 DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs,304 IFRHS->getInstallName()),305 "Install Name"));306 307 if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion())308 Output.push_back(recordDifferences(309 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(310 lhs, IFLHS->getCurrentVersion()),311 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(312 rhs, IFRHS->getCurrentVersion()),313 "Current Version"));314 if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion())315 Output.push_back(recordDifferences(316 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(317 lhs, IFLHS->getCompatibilityVersion()),318 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>(319 rhs, IFRHS->getCompatibilityVersion()),320 "Compatibility Version"));321 if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion())322 Output.push_back(323 recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(324 lhs, IFLHS->getSwiftABIVersion()),325 DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>(326 rhs, IFRHS->getSwiftABIVersion()),327 "Swift ABI Version"));328 329 if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace())330 Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(331 lhs, IFLHS->isTwoLevelNamespace()),332 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(333 rhs, IFRHS->isTwoLevelNamespace()),334 "Two Level Namespace"));335 336 if (IFLHS->isApplicationExtensionSafe() !=337 IFRHS->isApplicationExtensionSafe())338 Output.push_back(339 recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(340 lhs, IFLHS->isApplicationExtensionSafe()),341 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(342 rhs, IFRHS->isApplicationExtensionSafe()),343 "Application Extension Safe"));344 345 if (IFLHS->hasSimulatorSupport() != IFRHS->hasSimulatorSupport())346 Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(347 lhs, IFLHS->hasSimulatorSupport()),348 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(349 rhs, IFRHS->hasSimulatorSupport()),350 "Simulator Support"));351 352 if (IFLHS->isOSLibNotForSharedCache() != IFRHS->isOSLibNotForSharedCache())353 Output.push_back(354 recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>(355 lhs, IFLHS->isOSLibNotForSharedCache()),356 DiffScalarVal<bool, AD_Diff_Scalar_Bool>(357 rhs, IFRHS->isOSLibNotForSharedCache()),358 "Shared Cache Ineligible"));359 360 if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries())361 Output.push_back(recordDifferences(IFLHS->reexportedLibraries(),362 IFRHS->reexportedLibraries(),363 "Reexported Libraries"));364 365 if (IFLHS->rpaths() != IFRHS->rpaths())366 Output.push_back(recordDifferences(IFLHS->rpaths(), IFRHS->rpaths(),367 "Run Path Search Paths"));368 369 if (IFLHS->allowableClients() != IFRHS->allowableClients())370 Output.push_back(recordDifferences(IFLHS->allowableClients(),371 IFRHS->allowableClients(),372 "Allowable Clients"));373 374 if (IFLHS->umbrellas() != IFRHS->umbrellas())375 Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(),376 "Parent Umbrellas"));377 378 if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols()))379 Output.push_back(380 recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols"));381 382 if (IFLHS->documents() != IFRHS->documents()) {383 DiffOutput Docs("Inlined Reexported Frameworks/Libraries");384 Docs.Kind = AD_Inline_Doc;385 std::vector<StringRef> DocsInserted;386 // Iterate through inline frameworks/libraries from interface file and find387 // match based on install name.388 for (auto DocLHS : IFLHS->documents()) {389 auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) {390 return (DocLHS->getInstallName() == DocRHS->getInstallName());391 });392 // If a match found, recursively get differences between the pair.393 if (Pair != IFRHS->documents().end()) {394 InlineDoc PairDiff =395 InlineDoc(DocLHS->getInstallName(),396 findDifferences(DocLHS.get(), Pair->get()));397 if (!PairDiff.DocValues.empty())398 Docs.Values.push_back(399 std::make_unique<InlineDoc>(std::move(PairDiff)));400 }401 // No matching inlined library was found.402 else403 Docs.Values.push_back(std::make_unique<InlineDoc>(404 InlineDoc(DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs),405 /*IsMissingDoc=*/true)));406 DocsInserted.push_back(DocLHS->getInstallName());407 }408 for (auto DocRHS : IFRHS->documents()) {409 auto WasGathered =410 llvm::any_of(DocsInserted, [&](const auto &GatheredDoc) {411 return (GatheredDoc == DocRHS->getInstallName());412 });413 if (!WasGathered)414 Docs.Values.push_back(std::make_unique<InlineDoc>(415 InlineDoc(DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs),416 /*IsMissingDoc=*/true)));417 }418 if (!Docs.Values.empty())419 Output.push_back(std::move(Docs));420 }421 return Output;422}423 424template <typename T>425void printSingleVal(std::string Indent, const DiffOutput &Attr,426 raw_ostream &OS) {427 if (Attr.Values.empty())428 return;429 OS << Indent << Attr.Name << "\n";430 for (auto &RawItem : Attr.Values)431 if (T *Item = dyn_cast<T>(RawItem.get()))432 Item->print(OS, Indent);433}434 435template <typename T>436T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) {437 T *CastAttr = cast<T>(RawAttr.get());438 return CastAttr;439}440 441template <typename T> void sortTargetValues(std::vector<T> &TargValues) {442 llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) {443 if (ValA.getOrder() == ValB.getOrder()) {444 return ValA.getVal() < ValB.getVal();445 }446 return ValA.getOrder() < ValB.getOrder();447 });448}449 450template <typename T>451void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) {452 if (Attr.Values.empty()) {453 OS << Indent << "'" << Attr.Name << "' differ by order\n";454 return;455 }456 457 OS << Indent << Attr.Name << "\n";458 459 std::vector<T *> SortedAttrs;460 461 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>);462 463 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {464 return ValA->Targ < ValB->Targ;465 });466 467 for (auto *Vec : SortedAttrs) {468 sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(469 Vec->TargValues);470 OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n";471 for (auto &Item : Vec->TargValues)472 Item.print(OS, Indent);473 }474}475 476template <>477void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr,478 raw_ostream &OS) {479 if (Attr.Values.empty())480 return;481 482 OS << Indent << Attr.Name << "\n";483 484 std::vector<DiffSymVec *> SortedAttrs;485 486 llvm::transform(Attr.Values, std::back_inserter(SortedAttrs),487 castValues<DiffSymVec>);488 489 llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) {490 return ValA->Targ < ValB->Targ;491 });492 for (auto *SymVec : SortedAttrs) {493 sortTargetValues<SymScalar>(SymVec->TargValues);494 OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n";495 for (auto &Item : SymVec->TargValues)496 Item.print(OS, Indent, SymVec->Targ);497 }498}499 500void DiffEngine::printDifferences(raw_ostream &OS,501 const std::vector<DiffOutput> &Diffs,502 int IndentCounter) {503 std::string Indent = std::string(IndentCounter, '\t');504 for (auto &Attr : Diffs) {505 switch (Attr.Kind) {506 case AD_Diff_Scalar_Str:507 if (IndentCounter == 0)508 printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent,509 Attr, OS);510 break;511 case AD_Diff_Scalar_PackedVersion:512 printSingleVal<513 DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent,514 Attr, OS);515 break;516 case AD_Diff_Scalar_Unsigned:517 printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent,518 Attr, OS);519 break;520 case AD_Diff_Scalar_Bool:521 printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr,522 OS);523 break;524 case AD_Str_Vec:525 printVecVal<DiffStrVec>(Indent, Attr, OS);526 break;527 case AD_Sym_Vec:528 printVecVal<DiffSymVec>(Indent, Attr, OS);529 break;530 case AD_Inline_Doc:531 if (!Attr.Values.empty()) {532 OS << Indent << Attr.Name << "\n";533 for (auto &Item : Attr.Values) {534 if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get())) {535 if (Doc->DocValues.empty())536 continue;537 IndentCounter = 2;538 // When only one input file contains an inlined library, print out539 // the install name for it. Otherwise print out the different values540 // by the install name.541 if (Doc->IsMissingDoc) {542 printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(543 std::string(IndentCounter, '\t'), Doc->DocValues.front(), OS);544 } else {545 OS << Indent << "\t" << Doc->InstallName << "\n";546 printDifferences(OS, std::move(Doc->DocValues), IndentCounter);547 }548 }549 }550 }551 break;552 }553 }554}555 556bool DiffEngine::compareFiles(raw_ostream &OS) {557 if (*FileLHS == *FileRHS)558 return false;559 OS << "< " << std::string(FileLHS->getPath().data()) << "\n> "560 << std::string(FileRHS->getPath().data()) << "\n\n";561 std::vector<DiffOutput> Diffs = findDifferences(FileLHS, FileRHS);562 printDifferences(OS, Diffs, 0);563 return true;564}565