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 tbd_v1_file1[] = 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(tbd_v1_file1, "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 tbd_v1_file2[] = "--- !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(tbd_v1_file2, "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 tbd_v1_file3[] = 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(tbd_v1_file3, Buffer.c_str()); 197 } 198 199 TEST(TBDv1, Platform_macOS) { 200 static const char tbd_v1_platform_macos[] = "---\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(tbd_v1_platform_macos, "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 tbd_v1_platform_ios[] = "---\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(tbd_v1_platform_ios, "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 tbd_v1_platform_watchos[] = "---\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(tbd_v1_platform_watchos, "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 tbd_v1_platform_tvos[] = "---\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(tbd_v1_platform_tvos, "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 tbd_v1_platform_bridgeos[] = "---\n" 269 "archs: [ armv7k ]\n" 270 "platform: bridgeos\n" 271 "install-name: Test.dylib\n" 272 "...\n"; 273 274 auto Result = 275 TextAPIReader::get(MemoryBufferRef(tbd_v1_platform_bridgeos, "Test.tbd")); 276 EXPECT_TRUE(!!Result); 277 auto Platform = PlatformKind::bridgeOS; 278 auto File = std::move(Result.get()); 279 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 280 EXPECT_EQ(File->getPlatforms().size(), 1U); 281 EXPECT_EQ(Platform, *File->getPlatforms().begin()); 282 } 283 284 TEST(TBDv1, Swift_1_0) { 285 static const char tbd_v1_swift_1_0[] = "---\n" 286 "archs: [ arm64 ]\n" 287 "platform: ios\n" 288 "install-name: Test.dylib\n" 289 "swift-version: 1.0\n" 290 "...\n"; 291 292 auto Result = 293 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_1_0, "Test.tbd")); 294 EXPECT_TRUE(!!Result); 295 auto File = std::move(Result.get()); 296 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 297 EXPECT_EQ(1U, File->getSwiftABIVersion()); 298 } 299 300 TEST(TBDv1, Swift_1_1) { 301 static const char tbd_v1_swift_1_1[] = "---\n" 302 "archs: [ arm64 ]\n" 303 "platform: ios\n" 304 "install-name: Test.dylib\n" 305 "swift-version: 1.1\n" 306 "...\n"; 307 308 auto Result = 309 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_1_1, "Test.tbd")); 310 EXPECT_TRUE(!!Result); 311 auto File = std::move(Result.get()); 312 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 313 EXPECT_EQ(2U, File->getSwiftABIVersion()); 314 } 315 316 TEST(TBDv1, Swift_2_0) { 317 static const char tbd_v1_swift_2_0[] = "---\n" 318 "archs: [ arm64 ]\n" 319 "platform: ios\n" 320 "install-name: Test.dylib\n" 321 "swift-version: 2.0\n" 322 "...\n"; 323 324 auto Result = 325 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_2_0, "Test.tbd")); 326 EXPECT_TRUE(!!Result); 327 auto File = std::move(Result.get()); 328 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 329 EXPECT_EQ(3U, File->getSwiftABIVersion()); 330 } 331 332 TEST(TBDv1, Swift_3_0) { 333 static const char tbd_v1_swift_3_0[] = "---\n" 334 "archs: [ arm64 ]\n" 335 "platform: ios\n" 336 "install-name: Test.dylib\n" 337 "swift-version: 3.0\n" 338 "...\n"; 339 340 auto Result = 341 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_3_0, "Test.tbd")); 342 EXPECT_TRUE(!!Result); 343 auto File = std::move(Result.get()); 344 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 345 EXPECT_EQ(4U, File->getSwiftABIVersion()); 346 } 347 348 TEST(TBDv1, Swift_4_0) { 349 static const char tbd_v1_swift_4_0[] = "---\n" 350 "archs: [ arm64 ]\n" 351 "platform: ios\n" 352 "install-name: Test.dylib\n" 353 "swift-version: 4.0\n" 354 "...\n"; 355 356 auto Result = 357 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_4_0, "Test.tbd")); 358 EXPECT_FALSE(!!Result); 359 auto errorMessage = toString(Result.takeError()); 360 EXPECT_EQ("malformed file\nTest.tbd:5:16: error: invalid Swift ABI " 361 "version.\nswift-version: 4.0\n ^~~\n", 362 errorMessage); 363 } 364 365 TEST(TBDv1, Swift_5) { 366 static const char tbd_v1_swift_5[] = "---\n" 367 "archs: [ arm64 ]\n" 368 "platform: ios\n" 369 "install-name: Test.dylib\n" 370 "swift-version: 5\n" 371 "...\n"; 372 373 auto Result = TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_5, "Test.tbd")); 374 EXPECT_TRUE(!!Result); 375 auto File = std::move(Result.get()); 376 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 377 EXPECT_EQ(5U, File->getSwiftABIVersion()); 378 } 379 380 TEST(TBDv1, Swift_99) { 381 static const char tbd_v1_swift_99[] = "---\n" 382 "archs: [ arm64 ]\n" 383 "platform: ios\n" 384 "install-name: Test.dylib\n" 385 "swift-version: 99\n" 386 "...\n"; 387 388 auto Result = 389 TextAPIReader::get(MemoryBufferRef(tbd_v1_swift_99, "Test.tbd")); 390 EXPECT_TRUE(!!Result); 391 auto File = std::move(Result.get()); 392 EXPECT_EQ(FileType::TBD_V1, File->getFileType()); 393 EXPECT_EQ(99U, File->getSwiftABIVersion()); 394 } 395 396 TEST(TBDv1, UnknownArchitecture) { 397 static const char tbd_v1_file_unknown_architecture[] = 398 "---\n" 399 "archs: [ foo ]\n" 400 "platform: macosx\n" 401 "install-name: Test.dylib\n" 402 "...\n"; 403 404 auto Result = TextAPIReader::get( 405 MemoryBufferRef(tbd_v1_file_unknown_architecture, "Test.tbd")); 406 EXPECT_TRUE(!!Result); 407 } 408 409 TEST(TBDv1, UnknownPlatform) { 410 static const char tbd_v1_file_unknown_platform[] = "---\n" 411 "archs: [ i386 ]\n" 412 "platform: newOS\n" 413 "...\n"; 414 415 auto Result = TextAPIReader::get( 416 MemoryBufferRef(tbd_v1_file_unknown_platform, "Test.tbd")); 417 EXPECT_FALSE(!!Result); 418 auto errorMessage = toString(Result.takeError()); 419 EXPECT_EQ("malformed file\nTest.tbd:3:11: error: unknown platform\nplatform: " 420 "newOS\n ^~~~~\n", 421 errorMessage); 422 } 423 424 TEST(TBDv1, MalformedFile1) { 425 static const char malformed_file1[] = "---\n" 426 "archs: [ arm64 ]\n" 427 "foobar: \"Unsupported key\"\n" 428 "...\n"; 429 430 auto Result = 431 TextAPIReader::get(MemoryBufferRef(malformed_file1, "Test.tbd")); 432 EXPECT_FALSE(!!Result); 433 auto errorMessage = toString(Result.takeError()); 434 ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " 435 "'platform'\narchs: [ arm64 ]\n^\n", 436 errorMessage); 437 } 438 439 TEST(TBDv1, MalformedFile2) { 440 static const char malformed_file2[] = "---\n" 441 "archs: [ arm64 ]\n" 442 "platform: ios\n" 443 "install-name: Test.dylib\n" 444 "foobar: \"Unsupported key\"\n" 445 "...\n"; 446 447 auto Result = 448 TextAPIReader::get(MemoryBufferRef(malformed_file2, "Test.tbd")); 449 EXPECT_FALSE(!!Result); 450 auto errorMessage = toString(Result.takeError()); 451 ASSERT_EQ( 452 "malformed file\nTest.tbd:5:9: error: unknown key 'foobar'\nfoobar: " 453 "\"Unsupported key\"\n ^~~~~~~~~~~~~~~~~\n", 454 errorMessage); 455 } 456 457 } // end namespace TBDv1. 458