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