Skip to content

Commit

Permalink
[llvm][TextAPI] add equality operator for InterfaceFile
Browse files Browse the repository at this point in the history
This patch adds functionality to compare for the equality between `InterfaceFile`s based on attributes specific to linking.

Reviewed By: cishida, steven_wu

Differential Revision: https://reviews.llvm.org/D96629
  • Loading branch information
mcl0vinit authored and cyndyishida committed Feb 18, 2021
1 parent 508aa69 commit eb2eeeb
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 3 deletions.
27 changes: 24 additions & 3 deletions llvm/include/llvm/TextAPI/MachO/InterfaceFile.h
Expand Up @@ -336,9 +336,7 @@ class InterfaceFile {
/// Add a library for inlining to top level library.
///
///\param Document The library to inline with top level library.
void addDocument(std::shared_ptr<InterfaceFile> &&Document) {
Documents.emplace_back(std::move(Document));
}
void addDocument(std::shared_ptr<InterfaceFile> &&Document);

/// Get the list of inlined libraries.
///
Expand Down Expand Up @@ -397,6 +395,14 @@ class InterfaceFile {
fn);
}

/// The equality is determined by attributes that impact linking
/// compatibilities. UUIDs, Path, & FileKind are irrelevant since these by
/// itself should not impact linking.
/// This is an expensive operation.
bool operator==(const InterfaceFile &O) const;

bool operator!=(const InterfaceFile &O) const { return !(*this == O); }

private:
llvm::BumpPtrAllocator Allocator;
StringRef copyString(StringRef String) {
Expand Down Expand Up @@ -427,6 +433,21 @@ class InterfaceFile {
SymbolMapType Symbols;
};

template <typename DerivedT, typename KeyInfoT, typename BucketT>
bool operator==(const DenseMapBase<DerivedT, SymbolsMapKey, MachO::Symbol *,
KeyInfoT, BucketT> &LHS,
const DenseMapBase<DerivedT, SymbolsMapKey, MachO::Symbol *,
KeyInfoT, BucketT> &RHS) {
if (LHS.size() != RHS.size())
return false;
for (auto KV : LHS) {
auto I = RHS.find(KV.first);
if (I == RHS.end() || *I->second != *KV.second)
return false;
}
return true;
}

} // end namespace MachO.
} // end namespace llvm.

Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/TextAPI/MachO/Symbol.h
Expand Up @@ -104,6 +104,15 @@ class Symbol {
void dump() const { dump(llvm::errs()); }
#endif

bool operator==(const Symbol &O) const {
return (Kind == O.Kind) && (Name == O.Name) && (Targets == O.Targets) &&
(Flags == O.Flags);
}

bool operator!=(const Symbol &O) const {
return !(*this == O);
}

private:
StringRef Name;
TargetList Targets;
Expand Down
43 changes: 43 additions & 0 deletions llvm/lib/TextAPI/MachO/InterfaceFile.cpp
Expand Up @@ -117,3 +117,46 @@ void InterfaceFile::addSymbol(SymbolKind Kind, StringRef Name,
for (const auto &Target : Targets)
result.first->second->addTarget(Target);
}

void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
auto Pos = llvm::lower_bound(Documents, Document,
[](const std::shared_ptr<InterfaceFile> &LHS,
const std::shared_ptr<InterfaceFile> &RHS) {
return LHS->InstallName < RHS->InstallName;
});
Documents.insert(Pos, Document);
}

bool InterfaceFile::operator==(const InterfaceFile &O) const {
if (Targets != O.Targets)
return false;
if (InstallName != O.InstallName)
return false;
if ((CurrentVersion != O.CurrentVersion) ||
(CompatibilityVersion != O.CompatibilityVersion))
return false;
if (SwiftABIVersion != O.SwiftABIVersion)
return false;
if (IsTwoLevelNamespace != O.IsTwoLevelNamespace)
return false;
if (IsAppExtensionSafe != O.IsAppExtensionSafe)
return false;
if (IsInstallAPI != O.IsInstallAPI)
return false;
if (ParentUmbrellas != O.ParentUmbrellas)
return false;
if (AllowableClients != O.AllowableClients)
return false;
if (ReexportedLibraries != O.ReexportedLibraries)
return false;
if (Symbols != O.Symbols)
return false;
if (!std::equal(Documents.begin(), Documents.end(), O.Documents.begin(),
O.Documents.end(),
[](const std::shared_ptr<InterfaceFile> LHS,
const std::shared_ptr<InterfaceFile> RHS) {
return *LHS == *RHS;
}))
return false;
return true;
}
19 changes: 19 additions & 0 deletions llvm/unittests/TextAPI/TextStubHelpers.h
Expand Up @@ -40,5 +40,24 @@ inline std::string stripWhitespace(std::string S) {
S.erase(std::remove_if(S.begin(), S.end(), ::isspace), S.end());
return S;
}

// This will transform a single InterfaceFile then compare against the other
// InterfaceFile then transform the second InterfaceFile in the same way to
// regain equality.
inline bool
checkEqualityOnTransform(MachO::InterfaceFile &FileA,
MachO::InterfaceFile &FileB,
void (*Transform)(MachO::InterfaceFile *)) {
Transform(&FileA);
// Files should not be equal.
if (FileA == FileB)
return false;
Transform(&FileB);
// Files should be equal.
if (FileA != FileB)
return false;
return true;
}

} // namespace llvm
#endif
110 changes: 110 additions & 0 deletions llvm/unittests/TextAPI/TextStubV3Tests.cpp
Expand Up @@ -836,4 +836,114 @@ TEST(TBDv3, MalformedFile2) {
ErrorMessage);
}

TEST(TBDv3, InterfaceEquality) {
static const char TBDv3File[] =
"--- !tapi-tbd-v3\n"
"archs: [ armv7, arm64 ]\n"
"uuids: [ 'armv7: 00000000-0000-0000-0000-000000000000',\n"
" 'arm64: 11111111-1111-1111-1111-111111111111']\n"
"platform: ios\n"
"flags: [ installapi ]\n"
"install-name: Test.dylib\n"
"current-version: 2.3.4\n"
"compatibility-version: 1.0\n"
"swift-abi-version: 1.1\n"
"parent-umbrella: Umbrella.dylib\n"
"exports:\n"
" - archs: [ armv7, arm64 ]\n"
" allowable-clients: [ clientA ]\n"
" re-exports: [ /usr/lib/libfoo.dylib ]\n"
" symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n"
" objc-classes: [ class1, class2 ]\n"
" objc-eh-types: [ class1 ]\n"
" objc-ivars: [ class1._ivar1, class1._ivar2 ]\n"
" weak-def-symbols: [ _weak1, _weak2 ]\n"
" thread-local-symbols: [ _tlv1, _tlv3 ]\n"
" - archs: [ armv7 ]\n"
" symbols: [ _sym5 ]\n"
" objc-classes: [ class3 ]\n"
" objc-ivars: [ class1._ivar3 ]\n"
" weak-def-symbols: [ _weak3 ]\n"
" thread-local-symbols: [ _tlv3 ]\n"
"--- !tapi-tbd-v3\n"
"archs: [ i386 ]\n"
"platform: macosx\n"
"install-name: '/usr/lib/libbar.dylib'\n"
"current-version: 0\n"
"compatibility-version: 0\n"
"swift-abi-version: 5\n"
"objc-constraint: none\n"
"exports:\n"
" - archs: [ i386 ]\n"
" symbols: [ _sym3, _sym4 ]\n"
"...\n";
Expected<TBDFile> ResultA =
TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));
EXPECT_TRUE(!!ResultA);
InterfaceFile FileA = std::move(*ResultA.get());
Expected<TBDFile> ResultB =
TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));
EXPECT_TRUE(!!ResultB);
InterfaceFile FileB = std::move(*ResultB.get());
EXPECT_FALSE(FileA.getPath() == FileB.getPath());
EXPECT_TRUE(FileA == FileB);
}



TEST(TBDv3, InterfaceInequality) {
static const char TBDv3File[] = "--- !tapi-tbd-v3\n"
"archs: [ armv7, arm64 ]\n"
"platform: ios\n"
"install-name: Test.dylib\n"
"...\n";

Expected<TBDFile> ResultA =
TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestA.tbd"));
EXPECT_TRUE(!!ResultA);
InterfaceFile FileA = std::move(*ResultA.get());
Expected<TBDFile> ResultB =
TextAPIReader::get(MemoryBufferRef(TBDv3File, "TestB.tbd"));
EXPECT_TRUE(!!ResultB);
InterfaceFile FileB = std::move(*ResultB.get());

EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->addTarget(Target(AK_x86_64, PlatformKind::iOS));
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->setCurrentVersion(PackedVersion(1, 2, 3));
File->setCompatibilityVersion(PackedVersion(1, 0, 0));
}));
EXPECT_TRUE(checkEqualityOnTransform(
FileA, FileB, [](InterfaceFile *File) { File->setSwiftABIVersion(5); }));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->setTwoLevelNamespace(false);
}));
EXPECT_TRUE(checkEqualityOnTransform(
FileA, FileB, [](InterfaceFile *File) { File->setInstallAPI(true); }));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->setApplicationExtensionSafe(false);
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->addParentUmbrella(Target(AK_armv7, PlatformKind::iOS), "Umbrella.dylib");
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->addAllowableClient("ClientA", Target(AK_armv7, PlatformKind::iOS));
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->addReexportedLibrary("/System/Library/Frameworks/A.framework/A",
Target(AK_armv7, PlatformKind::iOS));
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
File->addSymbol(SymbolKind::GlobalSymbol, "_symA", {Target(AK_arm64, PlatformKind::iOS)});
}));
EXPECT_TRUE(checkEqualityOnTransform(FileA, FileB, [](InterfaceFile *File) {
InterfaceFile Document;
Document.addTargets(TargetList{Target(AK_armv7, PlatformKind::iOS),
Target(AK_arm64, PlatformKind::iOS)});
Document.setInstallName("/System/Library/Frameworks/A.framework/A");
File->addDocument(std::make_shared<InterfaceFile>(std::move(Document)));
}));
}

} // namespace TBDv3

0 comments on commit eb2eeeb

Please sign in to comment.