1 //===-- TextStubV1Tests.cpp - TBD V1 File Test ----------------------------===// 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 #include "TextStubHelpers.h" 10 #include "llvm/TextAPI/MachO/InterfaceFile.h" 11 #include "llvm/TextAPI/MachO/TextAPIReader.h" 12 #include "llvm/TextAPI/MachO/TextAPIWriter.h" 13 #include "gtest/gtest.h" 14 #include <string> 15 #include <vector> 16 17 using namespace llvm; 18 using namespace llvm::MachO; 19 20 static ExportedSymbol TBDv1Symbols[] = { 21 {SymbolKind::GlobalSymbol, "$ld$hide$os9.0$_sym1", false, false}, 22 {SymbolKind::GlobalSymbol, "_sym1", false, false}, 23 {SymbolKind::GlobalSymbol, "_sym2", false, false}, 24 {SymbolKind::GlobalSymbol, "_sym3", false, false}, 25 {SymbolKind::GlobalSymbol, "_sym4", false, false}, 26 {SymbolKind::GlobalSymbol, "_sym5", false, false}, 27 {SymbolKind::GlobalSymbol, "_tlv1", false, true}, 28 {SymbolKind::GlobalSymbol, "_tlv2", false, true}, 29 {SymbolKind::GlobalSymbol, "_tlv3", false, true}, 30 {SymbolKind::GlobalSymbol, "_weak1", true, false}, 31 {SymbolKind::GlobalSymbol, "_weak2", true, false}, 32 {SymbolKind::GlobalSymbol, "_weak3", true, false}, 33 {SymbolKind::ObjectiveCClass, "class1", false, false}, 34 {SymbolKind::ObjectiveCClass, "class2", false, false}, 35 {SymbolKind::ObjectiveCClass, "class3", false, false}, 36 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar1", false, false}, 37 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar2", false, false}, 38 {SymbolKind::ObjectiveCInstanceVariable, "class1._ivar3", false, false}, 39 }; 40 41 namespace TBDv1 { 42 43 TEST(TBDv1, ReadFile) { 44 static const char TBDv1File1[] = 45 "---\n" 46 "archs: [ armv7, armv7s, armv7k, arm64 ]\n" 47 "platform: ios\n" 48 "install-name: Test.dylib\n" 49 "current-version: 2.3.4\n" 50 "compatibility-version: 1.0\n" 51 "swift-version: 1.1\n" 52 "exports:\n" 53 " - archs: [ armv7, armv7s, armv7k, arm64 ]\n" 54 " allowed-clients: [ clientA ]\n" 55 " re-exports: [ /usr/lib/libfoo.dylib ]\n" 56 " symbols: [ _sym1, _sym2, _sym3, _sym4, $ld$hide$os9.0$_sym1 ]\n" 57 " objc-classes: [ _class1, _class2 ]\n" 58 " objc-ivars: [ _class1._ivar1, _class1._ivar2 ]\n" 59 " weak-def-symbols: [ _weak1, _weak2 ]\n" 60 " thread-local-symbols: [ _tlv1, _tlv2 ]\n" 61 " - archs: [ armv7, armv7s, armv7k ]\n" 62 " symbols: [ _sym5 ]\n" 63 " objc-classes: [ _class3 ]\n" 64 " objc-ivars: [ _class1._ivar3 ]\n" 65 " weak-def-symbols: [ _weak3 ]\n" 66 " thread-local-symbols: [ _tlv3 ]\n" 67 "...\n"; 68 69 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1File1, "Test.tbd")); 70 EXPECT_TRUE(!!Result); 71 auto File = std::move(Result.get()); 72 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 73 auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64; 74 auto Platform = PlatformKind::iOS; 75 TargetList Targets; 76 for (auto &&arch : Archs) 77 Targets.emplace_back(Target(arch, Platform)); 78 EXPECT_EQ(Archs, File->getArchitectures()); 79 EXPECT_EQ(File->getPlatforms().size(), 1U); 80 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 81 EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); 82 EXPECT_EQ(PackedVersion(2, 3, 4), File->getCurrentVersion()); 83 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); 84 EXPECT_EQ(2U, File->getSwiftABIVersion()); 85 EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint()); 86 EXPECT_TRUE(File->isTwoLevelNamespace()); 87 EXPECT_TRUE(File->isApplicationExtensionSafe()); 88 EXPECT_FALSE(File->isInstallAPI()); 89 InterfaceFileRef client("clientA", Targets); 90 InterfaceFileRef reexport("/usr/lib/libfoo.dylib", Targets); 91 EXPECT_EQ(1U, File->allowableClients().size()); 92 EXPECT_EQ(client, File->allowableClients().front()); 93 EXPECT_EQ(1U, File->reexportedLibraries().size()); 94 EXPECT_EQ(reexport, File->reexportedLibraries().front()); 95 96 ExportedSymbolSeq Exports; 97 for (const auto *Sym : File->symbols()) { 98 EXPECT_FALSE(Sym->isWeakReferenced()); 99 EXPECT_FALSE(Sym->isUndefined()); 100 Exports.emplace_back( 101 ExportedSymbol{Sym->getKind(), std::string(Sym->getName()), 102 Sym->isWeakDefined(), Sym->isThreadLocalValue()}); 103 } 104 llvm::sort(Exports.begin(), Exports.end()); 105 106 EXPECT_EQ(sizeof(TBDv1Symbols) / sizeof(ExportedSymbol), Exports.size()); 107 EXPECT_TRUE( 108 std::equal(Exports.begin(), Exports.end(), std::begin(TBDv1Symbols))); 109 110 File->addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]}); 111 File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", 112 {Targets[1]}); 113 } 114 115 TEST(TBDv1, ReadFile2) { 116 static const char TBDv1File2[] = "--- !tapi-tbd-v1\n" 117 "archs: [ armv7, armv7s, armv7k, arm64 ]\n" 118 "platform: ios\n" 119 "install-name: Test.dylib\n" 120 "...\n"; 121 122 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1File2, "Test.tbd")); 123 EXPECT_TRUE(!!Result); 124 auto File = std::move(Result.get()); 125 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 126 auto Archs = AK_armv7 | AK_armv7s | AK_armv7k | AK_arm64; 127 auto Platform = PlatformKind::iOS; 128 TargetList Targets; 129 for (auto &&arch : Archs) 130 Targets.emplace_back(Target(arch, Platform)); 131 EXPECT_EQ(Archs, File->getArchitectures()); 132 EXPECT_EQ(File->getPlatforms().size(), 1U); 133 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 134 EXPECT_EQ(std::string("Test.dylib"), File->getInstallName()); 135 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); 136 EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); 137 EXPECT_EQ(0U, File->getSwiftABIVersion()); 138 EXPECT_EQ(ObjCConstraintType::None, File->getObjCConstraint()); 139 EXPECT_TRUE(File->isTwoLevelNamespace()); 140 EXPECT_TRUE(File->isApplicationExtensionSafe()); 141 EXPECT_FALSE(File->isInstallAPI()); 142 EXPECT_EQ(0U, File->allowableClients().size()); 143 EXPECT_EQ(0U, File->reexportedLibraries().size()); 144 } 145 146 TEST(TBDv1, WriteFile) { 147 static const char TBDv1File3[] = 148 "---\n" 149 "archs: [ i386, x86_64 ]\n" 150 "platform: macosx\n" 151 "install-name: '/usr/lib/libfoo.dylib'\n" 152 "current-version: 1.2.3\n" 153 "compatibility-version: 0\n" 154 "swift-version: 5\n" 155 "objc-constraint: retain_release\n" 156 "exports:\n" 157 " - archs: [ i386 ]\n" 158 " symbols: [ _sym1 ]\n" 159 " weak-def-symbols: [ _sym2 ]\n" 160 " thread-local-symbols: [ _sym3 ]\n" 161 " - archs: [ x86_64 ]\n" 162 " allowed-clients: [ clientA ]\n" 163 " re-exports: [ '/usr/lib/libfoo.dylib' ]\n" 164 " symbols: [ '_OBJC_EHTYPE_$_Class1' ]\n" 165 " objc-classes: [ _Class1 ]\n" 166 " objc-ivars: [ _Class1._ivar1 ]\n" 167 "...\n"; 168 169 InterfaceFile File; 170 TargetList Targets; 171 for (auto &&arch : AK_i386 | AK_x86_64) 172 Targets.emplace_back(Target(arch, PlatformKind::macOS)); 173 File.setPath("libfoo.dylib"); 174 File.setInstallName("/usr/lib/libfoo.dylib"); 175 File.setFileType(FileType::TBD_V1); 176 File.addTargets(Targets); 177 File.setCurrentVersion(PackedVersion(1, 2, 3)); 178 File.setSwiftABIVersion(5); 179 File.setObjCConstraint(ObjCConstraintType::Retain_Release); 180 File.addAllowableClient("clientA", Targets[1]); 181 File.addReexportedLibrary("/usr/lib/libfoo.dylib", Targets[1]); 182 File.addSymbol(SymbolKind::GlobalSymbol, "_sym1", {Targets[0]}); 183 File.addSymbol(SymbolKind::GlobalSymbol, "_sym2", {Targets[0]}, 184 SymbolFlags::WeakDefined); 185 File.addSymbol(SymbolKind::GlobalSymbol, "_sym3", {Targets[0]}, 186 SymbolFlags::ThreadLocalValue); 187 File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[1]}); 188 File.addSymbol(SymbolKind::ObjectiveCClassEHType, "Class1", {Targets[1]}); 189 File.addSymbol(SymbolKind::ObjectiveCInstanceVariable, "Class1._ivar1", 190 {Targets[1]}); 191 192 SmallString<4096> Buffer; 193 raw_svector_ostream OS(Buffer); 194 auto Result = TextAPIWriter::writeToStream(OS, File); 195 EXPECT_FALSE(Result); 196 EXPECT_STREQ(TBDv1File3, Buffer.c_str()); 197 } 198 199 TEST(TBDv1, Platform_macOS) { 200 static const char TBDv1PlatformMacOS[] = "---\n" 201 "archs: [ x86_64 ]\n" 202 "platform: macosx\n" 203 "install-name: Test.dylib\n" 204 "...\n"; 205 206 auto Result = 207 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformMacOS, "Test.tbd")); 208 EXPECT_TRUE(!!Result); 209 auto Platform = PlatformKind::macOS; 210 auto File = std::move(Result.get()); 211 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 212 EXPECT_EQ(File->getPlatforms().size(), 1U); 213 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 214 } 215 216 TEST(TBDv1, Platform_iOS) { 217 static const char TBDv1PlatformiOS[] = "---\n" 218 "archs: [ arm64 ]\n" 219 "platform: ios\n" 220 "install-name: Test.dylib\n" 221 "...\n"; 222 223 auto Result = 224 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformiOS, "Test.tbd")); 225 EXPECT_TRUE(!!Result); 226 auto Platform = PlatformKind::iOS; 227 auto File = std::move(Result.get()); 228 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 229 EXPECT_EQ(File->getPlatforms().size(), 1U); 230 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 231 } 232 233 TEST(TBDv1, Platform_watchOS) { 234 static const char TBDv1PlatformWatchOS[] = "---\n" 235 "archs: [ armv7k ]\n" 236 "platform: watchos\n" 237 "install-name: Test.dylib\n" 238 "...\n"; 239 240 auto Result = 241 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformWatchOS, "Test.tbd")); 242 EXPECT_TRUE(!!Result); 243 auto Platform = PlatformKind::watchOS; 244 auto File = std::move(Result.get()); 245 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 246 EXPECT_EQ(File->getPlatforms().size(), 1U); 247 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 248 } 249 250 TEST(TBDv1, Platform_tvOS) { 251 static const char TBDv1PlatformtvOS[] = "---\n" 252 "archs: [ arm64 ]\n" 253 "platform: tvos\n" 254 "install-name: Test.dylib\n" 255 "...\n"; 256 257 auto Result = 258 TextAPIReader::get(MemoryBufferRef(TBDv1PlatformtvOS, "Test.tbd")); 259 EXPECT_TRUE(!!Result); 260 auto Platform = PlatformKind::tvOS; 261 auto File = std::move(Result.get()); 262 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 263 EXPECT_EQ(File->getPlatforms().size(), 1U); 264 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 265 } 266 267 TEST(TBDv1, Platform_bridgeOS) { 268 static const char TBDv1BridgeOS[] = "---\n" 269 "archs: [ armv7k ]\n" 270 "platform: bridgeos\n" 271 "install-name: Test.dylib\n" 272 "...\n"; 273 274 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1BridgeOS, "Test.tbd")); 275 EXPECT_TRUE(!!Result); 276 auto Platform = PlatformKind::bridgeOS; 277 auto File = std::move(Result.get()); 278 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 279 EXPECT_EQ(File->getPlatforms().size(), 1U); 280 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 281 } 282 283 TEST(TBDv1, Swift_1_0) { 284 static const char TBDv1Swift1[] = "---\n" 285 "archs: [ arm64 ]\n" 286 "platform: ios\n" 287 "install-name: Test.dylib\n" 288 "swift-version: 1.0\n" 289 "...\n"; 290 291 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift1, "Test.tbd")); 292 EXPECT_TRUE(!!Result); 293 auto File = std::move(Result.get()); 294 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 295 EXPECT_EQ(1U, File->getSwiftABIVersion()); 296 } 297 298 TEST(TBDv1, Swift_1_1) { 299 static const char TBDv1Swift1dot[] = "---\n" 300 "archs: [ arm64 ]\n" 301 "platform: ios\n" 302 "install-name: Test.dylib\n" 303 "swift-version: 1.1\n" 304 "...\n"; 305 306 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift1dot, "Test.tbd")); 307 EXPECT_TRUE(!!Result); 308 auto File = std::move(Result.get()); 309 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 310 EXPECT_EQ(2U, File->getSwiftABIVersion()); 311 } 312 313 TEST(TBDv1, Swift_2_0) { 314 static const char TBDv1Swift2[] = "---\n" 315 "archs: [ arm64 ]\n" 316 "platform: ios\n" 317 "install-name: Test.dylib\n" 318 "swift-version: 2.0\n" 319 "...\n"; 320 321 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift2, "Test.tbd")); 322 EXPECT_TRUE(!!Result); 323 auto File = std::move(Result.get()); 324 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 325 EXPECT_EQ(3U, File->getSwiftABIVersion()); 326 } 327 328 TEST(TBDv1, Swift_3_0) { 329 static const char TBDv1Swift3[] = "---\n" 330 "archs: [ arm64 ]\n" 331 "platform: ios\n" 332 "install-name: Test.dylib\n" 333 "swift-version: 3.0\n" 334 "...\n"; 335 336 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift3, "Test.tbd")); 337 EXPECT_TRUE(!!Result); 338 auto File = std::move(Result.get()); 339 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 340 EXPECT_EQ(4U, File->getSwiftABIVersion()); 341 } 342 343 TEST(TBDv1, Swift_4_0) { 344 static const char TBDv1Swift4[] = "---\n" 345 "archs: [ arm64 ]\n" 346 "platform: ios\n" 347 "install-name: Test.dylib\n" 348 "swift-version: 4.0\n" 349 "...\n"; 350 351 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift4, "Test.tbd")); 352 EXPECT_FALSE(!!Result); 353 auto errorMessage = toString(Result.takeError()); 354 EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI " 355 "version.\nswift-version: 4.0\n ^~~\n", 356 errorMessage); 357 } 358 359 TEST(TBDv1, Swift_5) { 360 static const char TBDv1Swift5[] = "---\n" 361 "archs: [ arm64 ]\n" 362 "platform: ios\n" 363 "install-name: Test.dylib\n" 364 "swift-version: 5\n" 365 "...\n"; 366 367 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift5, "Test.tbd")); 368 EXPECT_TRUE(!!Result); 369 auto File = std::move(Result.get()); 370 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 371 EXPECT_EQ(5U, File->getSwiftABIVersion()); 372 } 373 374 TEST(TBDv1, Swift_99) { 375 static const char TBDv1Swift99[] = "---\n" 376 "archs: [ arm64 ]\n" 377 "platform: ios\n" 378 "install-name: Test.dylib\n" 379 "swift-version: 99\n" 380 "...\n"; 381 382 auto Result = TextAPIReader::get(MemoryBufferRef(TBDv1Swift99, "Test.tbd")); 383 EXPECT_TRUE(!!Result); 384 auto File = std::move(Result.get()); 385 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 386 EXPECT_EQ(99U, File->getSwiftABIVersion()); 387 } 388 389 TEST(TBDv1, UnknownArchitecture) { 390 static const char TBDv1FileUnknownArch[] = "---\n" 391 "archs: [ foo ]\n" 392 "platform: macosx\n" 393 "install-name: Test.dylib\n" 394 "...\n"; 395 396 auto Result = 397 TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownArch, "Test.tbd")); 398 EXPECT_TRUE(!!Result); 399 } 400 401 TEST(TBDv1, UnknownPlatform) { 402 static const char TBDv1FileUnknownPlatform[] = "---\n" 403 "archs: [ i386 ]\n" 404 "platform: newOS\n" 405 "...\n"; 406 407 auto Result = 408 TextAPIReader::get(MemoryBufferRef(TBDv1FileUnknownPlatform, "Test.tbd")); 409 EXPECT_FALSE(!!Result); 410 auto errorMessage = toString(Result.takeError()); 411 EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: " 412 "newOS\n ^~~~~\n", 413 errorMessage); 414 } 415 416 TEST(TBDv1, MalformedFile1) { 417 static const char TBDv1FileMalformed1[] = "---\n" 418 "archs: [ arm64 ]\n" 419 "foobar: \"Unsupported key\"\n" 420 "...\n"; 421 422 auto Result = 423 TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed1, "Test.tbd")); 424 EXPECT_FALSE(!!Result); 425 auto errorMessage = toString(Result.takeError()); 426 ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " 427 "'platform'\narchs: [ arm64 ]\n^\n", 428 errorMessage); 429 } 430 431 TEST(TBDv1, MalformedFile2) { 432 static const char TBDv1FileMalformed2[] = "---\n" 433 "archs: [ arm64 ]\n" 434 "platform: ios\n" 435 "install-name: Test.dylib\n" 436 "foobar: \"Unsupported key\"\n" 437 "...\n"; 438 439 auto Result = 440 TextAPIReader::get(MemoryBufferRef(TBDv1FileMalformed2, "Test.tbd")); 441 EXPECT_FALSE(!!Result); 442 auto errorMessage = toString(Result.takeError()); 443 ASSERT_EQ( 444 "malformed file\nTest.tbd:5:9: error: unknown key 'foobar'\nfoobar: " 445 "\"Unsupported key\"\n ^~~~~~~~~~~~~~~~~\n", 446 errorMessage); 447 } 448 449 } // end namespace TBDv1. 450