1//===- TextStubV5.cpp -----------------------------------------------------===//
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// Implements Text Stub JSON mappings.
10//
11//===----------------------------------------------------------------------===//
12#include "TextStubCommon.h"
13#include "llvm/ADT/StringSwitch.h"
14#include "llvm/Support/JSON.h"
15#include <optional>
16#include <utility>
17
18// clang-format off
19/*
20
21JSON Format specification.
22
23All library level keys, accept target values and are defaulted if not specified.
24
25{
26"tapi_tbd_version": 5, # Required: TBD version for all documents in file
27"main_library": { # Required: top level library
28 "target_info": [ # Required: target information
29 {
30 "target": "x86_64-macos",
31 "min_deployment": "10.14" # Optional: minOS defaults to 0
32 },
33 {
34 "target": "arm64-macos",
35 "min_deployment": "10.14"
36 },
37 {
38 "target": "arm64-maccatalyst",
39 "min_deployment": "12.1"
40 }],
41 "flags":[{"attributes": ["flat_namespace"]}], # Optional:
42 "install_names":[{"name":"/S/L/F/Foo.fwk/Foo"}], # Required: library install name
43 "current_versions":[{"version": "1.2"}], # Optional: defaults to 1
44 "compatibility_versions":[{ "version": "1.1"}], # Optional: defaults to 1
45 "rpaths": [ # Optional:
46 {
47 "targets": ["x86_64-macos"], # Optional: defaults to targets in `target-info`
48 "paths": ["@executable_path/.../Frameworks"]
49 }],
50 "parent_umbrellas": [{"umbrella": "System"}],
51 "allowable_clients": [{"clients": ["ClientA"]}],
52 "reexported_libraries": [{"names": ["/u/l/l/foo.dylib"]}],
53 "exported_symbols": [{ # List of export symbols section
54 "targets": ["x86_64-macos", "arm64-macos"], # Optional: defaults to targets in `target-info`
55 "text": { # List of Text segment symbols
56 "global": [ "_func" ],
57 "weak": [],
58 "thread_local": []
59 },
60 "data": { ... }, # List of Data segment symbols
61 }],
62 "reexported_symbols": [{ ... }], # List of reexported symbols section
63 "undefined_symbols": [{ ... }] # List of undefined symbols section
64},
65"libraries": [ # Optional: Array of inlined libraries
66 {...}, {...}, {...}
67]
68}
69*/
70// clang-format on
71
72using namespace llvm;
73using namespace llvm::json;
74using namespace llvm::MachO;
75
76namespace {
77struct JSONSymbol {
78 EncodeKind Kind;
79 std::string Name;
80 SymbolFlags Flags;
81};
82
83using AttrToTargets = std::map<std::string, TargetList>;
84using TargetsToSymbols =
85 SmallVector<std::pair<TargetList, std::vector<JSONSymbol>>>;
86
87/// Wrapper over a vector for handling textstub attributes, mapped to target
88/// triples, that require insertion order to be intact in the resulting \c
89/// InterfaceFile.
90class InOrderAttrToTargets {
91 using EntryT = std::pair<std::string, TargetList>;
92
93public:
94 void insert(EntryT &&Entry) {
95 auto &Element = get(Key&: Entry.first);
96 Element.second = Entry.second;
97 }
98
99 const EntryT *begin() { return Container.begin(); }
100 const EntryT *end() { return Container.end(); }
101
102private:
103 EntryT &get(std::string &Key) {
104 auto *It = find_if(Range&: Container,
105 P: [&Key](EntryT &Input) { return Input.first == Key; });
106 if (It != Container.end())
107 return *It;
108 Container.push_back(Elt: EntryT(Key, {}));
109 return Container.back();
110 }
111 llvm::SmallVector<EntryT> Container;
112};
113
114enum TBDKey : size_t {
115 TBDVersion = 0U,
116 MainLibrary,
117 Documents,
118 TargetInfo,
119 Targets,
120 Target,
121 Deployment,
122 Flags,
123 Attributes,
124 InstallName,
125 CurrentVersion,
126 CompatibilityVersion,
127 Version,
128 SwiftABI,
129 ABI,
130 ParentUmbrella,
131 Umbrella,
132 AllowableClients,
133 Clients,
134 ReexportLibs,
135 Names,
136 Name,
137 Exports,
138 Reexports,
139 Undefineds,
140 Data,
141 Text,
142 Weak,
143 ThreadLocal,
144 Globals,
145 ObjCClass,
146 ObjCEHType,
147 ObjCIvar,
148 RPath,
149 Paths,
150};
151
152std::array<StringRef, 64> Keys = {
153 "tapi_tbd_version",
154 "main_library",
155 "libraries",
156 "target_info",
157 "targets",
158 "target",
159 "min_deployment",
160 "flags",
161 "attributes",
162 "install_names",
163 "current_versions",
164 "compatibility_versions",
165 "version",
166 "swift_abi",
167 "abi",
168 "parent_umbrellas",
169 "umbrella",
170 "allowable_clients",
171 "clients",
172 "reexported_libraries",
173 "names",
174 "name",
175 "exported_symbols",
176 "reexported_symbols",
177 "undefined_symbols",
178 "data",
179 "text",
180 "weak",
181 "thread_local",
182 "global",
183 "objc_class",
184 "objc_eh_type",
185 "objc_ivar",
186 "rpaths",
187 "paths",
188};
189
190static llvm::SmallString<128> getParseErrorMsg(TBDKey Key) {
191 return {"invalid ", Keys[Key], " section"};
192}
193
194static llvm::SmallString<128> getSerializeErrorMsg(TBDKey Key) {
195 return {"missing ", Keys[Key], " information"};
196}
197
198class JSONStubError : public llvm::ErrorInfo<llvm::json::ParseError> {
199public:
200 JSONStubError(Twine ErrMsg) : Message(ErrMsg.str()) {}
201
202 void log(llvm::raw_ostream &OS) const override { OS << Message << "\n"; }
203 std::error_code convertToErrorCode() const override {
204 return llvm::inconvertibleErrorCode();
205 }
206
207private:
208 std::string Message;
209};
210
211template <typename JsonT, typename StubT = JsonT>
212Expected<StubT> getRequiredValue(
213 TBDKey Key, const Object *Obj,
214 std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
215 std::function<std::optional<StubT>(JsonT)> Validate = nullptr) {
216 std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
217 if (!Val)
218 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
219
220 if (Validate == nullptr)
221 return static_cast<StubT>(*Val);
222
223 std::optional<StubT> Result = Validate(*Val);
224 if (!Result.has_value())
225 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
226 return Result.value();
227}
228
229template <typename JsonT, typename StubT = JsonT>
230Expected<StubT> getRequiredValue(
231 TBDKey Key, const Object *Obj,
232 std::function<std::optional<JsonT>(const Object *, StringRef)> const
233 GetValue,
234 StubT DefaultValue, function_ref<std::optional<StubT>(JsonT)> Validate) {
235 std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
236 if (!Val)
237 return DefaultValue;
238
239 std::optional<StubT> Result;
240 Result = Validate(*Val);
241 if (!Result.has_value())
242 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
243 return Result.value();
244}
245
246Error collectFromArray(TBDKey Key, const Object *Obj,
247 function_ref<void(StringRef)> Append,
248 bool IsRequired = false) {
249 const auto *Values = Obj->getArray(K: Keys[Key]);
250 if (!Values) {
251 if (IsRequired)
252 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
253 return Error::success();
254 }
255
256 for (const Value &Val : *Values) {
257 auto ValStr = Val.getAsString();
258 if (!ValStr.has_value())
259 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
260 Append(ValStr.value());
261 }
262
263 return Error::success();
264}
265
266namespace StubParser {
267
268Expected<FileType> getVersion(const Object *File) {
269 auto VersionOrErr = getRequiredValue<int64_t, FileType>(
270 Key: TBDKey::TBDVersion, Obj: File, GetValue: &Object::getInteger,
271 Validate: [](int64_t Val) -> std::optional<FileType> {
272 unsigned Result = Val;
273 if (Result != 5)
274 return std::nullopt;
275 return FileType::TBD_V5;
276 });
277
278 if (!VersionOrErr)
279 return VersionOrErr.takeError();
280 return *VersionOrErr;
281}
282
283Expected<std::optional<MachO::Target>> parseTargetStr(StringRef Str) {
284 auto TargetOrErr = MachO::Target::create(Target: Str);
285 if (!TargetOrErr)
286 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Target));
287 if (!TargetOrErr->isValid())
288 return std::nullopt;
289 return *TargetOrErr;
290}
291
292Expected<TargetList> getTargets(const Object *Section) {
293 const auto *Targets = Section->getArray(K: Keys[TBDKey::Targets]);
294 if (!Targets)
295 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Targets));
296
297 TargetList IFTargets;
298 for (const Value &JSONTarget : *Targets) {
299 auto TargetStr = JSONTarget.getAsString();
300 if (!TargetStr.has_value())
301 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Target));
302 auto TargetOrErr = parseTargetStr(Str: TargetStr.value());
303 if (!TargetOrErr)
304 return TargetOrErr.takeError();
305 if (!TargetOrErr->has_value())
306 continue;
307 IFTargets.push_back(Elt: **TargetOrErr);
308 }
309 return std::move(IFTargets);
310}
311
312Expected<TargetList> getTargetsSection(const Object *Section) {
313 const Array *Targets = Section->getArray(K: Keys[TBDKey::TargetInfo]);
314 if (!Targets)
315 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Targets));
316
317 TargetList IFTargets;
318 for (const Value &JSONTarget : *Targets) {
319 const auto *Obj = JSONTarget.getAsObject();
320 if (!Obj)
321 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Target));
322 auto TargetStr =
323 getRequiredValue<StringRef>(Key: TBDKey::Target, Obj, GetValue: &Object::getString);
324 if (!TargetStr)
325 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Target));
326 auto TargetOrErr = parseTargetStr(Str: *TargetStr);
327 if (!TargetOrErr)
328 return TargetOrErr.takeError();
329 if (!TargetOrErr->has_value())
330 continue;
331
332 auto VersionStr = Obj->getString(K: Keys[TBDKey::Deployment]);
333 VersionTuple Version;
334 if (VersionStr && Version.tryParse(string: *VersionStr))
335 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Deployment));
336 (*TargetOrErr)->MinDeployment = Version;
337
338 // Convert to LLVM::Triple to accurately compute minOS + platform + arch
339 // pairing.
340 IFTargets.push_back(
341 Elt: MachO::Target(Triple(getTargetTripleName(Targ: **TargetOrErr))));
342 }
343 return std::move(IFTargets);
344}
345
346Error collectSymbolsFromSegment(const Object *Segment, TargetsToSymbols &Result,
347 SymbolFlags SectionFlag) {
348 auto Err = collectFromArray(
349 Key: TBDKey::Globals, Obj: Segment, Append: [&Result, &SectionFlag](StringRef Name) {
350 JSONSymbol Sym = {.Kind: EncodeKind::GlobalSymbol, .Name: Name.str(), .Flags: SectionFlag};
351 Result.back().second.emplace_back(args&: Sym);
352 });
353 if (Err)
354 return Err;
355
356 Err = collectFromArray(
357 Key: TBDKey::ObjCClass, Obj: Segment, Append: [&Result, &SectionFlag](StringRef Name) {
358 JSONSymbol Sym = {.Kind: EncodeKind::ObjectiveCClass, .Name: Name.str(), .Flags: SectionFlag};
359 Result.back().second.emplace_back(args&: Sym);
360 });
361 if (Err)
362 return Err;
363
364 Err = collectFromArray(Key: TBDKey::ObjCEHType, Obj: Segment,
365 Append: [&Result, &SectionFlag](StringRef Name) {
366 JSONSymbol Sym = {.Kind: EncodeKind::ObjectiveCClassEHType,
367 .Name: Name.str(), .Flags: SectionFlag};
368 Result.back().second.emplace_back(args&: Sym);
369 });
370 if (Err)
371 return Err;
372
373 Err = collectFromArray(
374 Key: TBDKey::ObjCIvar, Obj: Segment, Append: [&Result, &SectionFlag](StringRef Name) {
375 JSONSymbol Sym = {.Kind: EncodeKind::ObjectiveCInstanceVariable, .Name: Name.str(),
376 .Flags: SectionFlag};
377 Result.back().second.emplace_back(args&: Sym);
378 });
379 if (Err)
380 return Err;
381
382 SymbolFlags WeakFlag =
383 SectionFlag |
384 (((SectionFlag & SymbolFlags::Undefined) == SymbolFlags::Undefined)
385 ? SymbolFlags::WeakReferenced
386 : SymbolFlags::WeakDefined);
387 Err = collectFromArray(
388 Key: TBDKey::Weak, Obj: Segment, Append: [&Result, WeakFlag](StringRef Name) {
389 JSONSymbol Sym = {.Kind: EncodeKind::GlobalSymbol, .Name: Name.str(), .Flags: WeakFlag};
390 Result.back().second.emplace_back(args&: Sym);
391 });
392 if (Err)
393 return Err;
394
395 Err = collectFromArray(
396 Key: TBDKey::ThreadLocal, Obj: Segment, Append: [&Result, SectionFlag](StringRef Name) {
397 JSONSymbol Sym = {.Kind: EncodeKind::GlobalSymbol, .Name: Name.str(),
398 .Flags: SymbolFlags::ThreadLocalValue | SectionFlag};
399 Result.back().second.emplace_back(args&: Sym);
400 });
401 if (Err)
402 return Err;
403
404 return Error::success();
405}
406
407Expected<StringRef> getNameSection(const Object *File) {
408 const Array *Section = File->getArray(K: Keys[TBDKey::InstallName]);
409 if (!Section)
410 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::InstallName));
411
412 assert(!Section->empty() && "unexpected missing install name");
413 // TODO: Just take first for now.
414 const auto *Obj = Section->front().getAsObject();
415 if (!Obj)
416 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::InstallName));
417
418 return getRequiredValue<StringRef>(Key: TBDKey::Name, Obj, GetValue: &Object::getString);
419}
420
421Expected<TargetsToSymbols> getSymbolSection(const Object *File, TBDKey Key,
422 TargetList &Targets) {
423
424 const Array *Section = File->getArray(K: Keys[Key]);
425 if (!Section)
426 return TargetsToSymbols();
427
428 SymbolFlags SectionFlag;
429 switch (Key) {
430 case TBDKey::Reexports:
431 SectionFlag = SymbolFlags::Rexported;
432 break;
433 case TBDKey::Undefineds:
434 SectionFlag = SymbolFlags::Undefined;
435 break;
436 default:
437 SectionFlag = SymbolFlags::None;
438 break;
439 };
440
441 TargetsToSymbols Result;
442 TargetList MappedTargets;
443 for (auto Val : *Section) {
444 auto *Obj = Val.getAsObject();
445 if (!Obj)
446 continue;
447
448 auto TargetsOrErr = getTargets(Section: Obj);
449 if (!TargetsOrErr) {
450 MappedTargets = Targets;
451 consumeError(Err: TargetsOrErr.takeError());
452 } else {
453 MappedTargets = *TargetsOrErr;
454 }
455 Result.emplace_back(
456 Args: std::make_pair(x: std::move(MappedTargets), y: std::vector<JSONSymbol>()));
457
458 auto *DataSection = Obj->getObject(K: Keys[TBDKey::Data]);
459 auto *TextSection = Obj->getObject(K: Keys[TBDKey::Text]);
460 // There should be at least one valid section.
461 if (!DataSection && !TextSection)
462 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
463
464 if (DataSection) {
465 auto Err = collectSymbolsFromSegment(Segment: DataSection, Result,
466 SectionFlag: SectionFlag | SymbolFlags::Data);
467 if (Err)
468 return std::move(Err);
469 }
470 if (TextSection) {
471 auto Err = collectSymbolsFromSegment(Segment: TextSection, Result,
472 SectionFlag: SectionFlag | SymbolFlags::Text);
473 if (Err)
474 return std::move(Err);
475 }
476 }
477
478 return std::move(Result);
479}
480
481template <typename ReturnT = AttrToTargets>
482Expected<ReturnT> getLibSection(const Object *File, TBDKey Key, TBDKey SubKey,
483 const TargetList &Targets) {
484 auto *Section = File->getArray(K: Keys[Key]);
485 if (!Section)
486 return ReturnT();
487
488 ReturnT Result;
489 TargetList MappedTargets;
490 for (auto Val : *Section) {
491 auto *Obj = Val.getAsObject();
492 if (!Obj)
493 continue;
494
495 auto TargetsOrErr = getTargets(Section: Obj);
496 if (!TargetsOrErr) {
497 MappedTargets = Targets;
498 consumeError(Err: TargetsOrErr.takeError());
499 } else {
500 MappedTargets = *TargetsOrErr;
501 }
502 auto Err =
503 collectFromArray(SubKey, Obj, [&Result, &MappedTargets](StringRef Key) {
504 Result.insert({Key.str(), MappedTargets});
505 });
506 if (Err)
507 return std::move(Err);
508 }
509
510 return std::move(Result);
511}
512
513Expected<AttrToTargets> getUmbrellaSection(const Object *File,
514 const TargetList &Targets) {
515 const auto *Umbrella = File->getArray(K: Keys[TBDKey::ParentUmbrella]);
516 if (!Umbrella)
517 return AttrToTargets();
518
519 AttrToTargets Result;
520 TargetList MappedTargets;
521 for (auto Val : *Umbrella) {
522 auto *Obj = Val.getAsObject();
523 if (!Obj)
524 return make_error<JSONStubError>(
525 Args: getParseErrorMsg(Key: TBDKey::ParentUmbrella));
526
527 // Get Targets section.
528 auto TargetsOrErr = getTargets(Section: Obj);
529 if (!TargetsOrErr) {
530 MappedTargets = Targets;
531 consumeError(Err: TargetsOrErr.takeError());
532 } else {
533 MappedTargets = *TargetsOrErr;
534 }
535
536 auto UmbrellaOrErr =
537 getRequiredValue<StringRef>(Key: TBDKey::Umbrella, Obj, GetValue: &Object::getString);
538 if (!UmbrellaOrErr)
539 return UmbrellaOrErr.takeError();
540 Result[UmbrellaOrErr->str()] = Targets;
541 }
542 return std::move(Result);
543}
544
545Expected<uint8_t> getSwiftVersion(const Object *File) {
546 const Array *Versions = File->getArray(K: Keys[TBDKey::SwiftABI]);
547 if (!Versions)
548 return 0;
549
550 for (const auto &Val : *Versions) {
551 const auto *Obj = Val.getAsObject();
552 if (!Obj)
553 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::SwiftABI));
554
555 // TODO: Take first for now.
556 return getRequiredValue<int64_t, uint8_t>(Key: TBDKey::ABI, Obj,
557 GetValue: &Object::getInteger);
558 }
559
560 return 0;
561}
562
563Expected<PackedVersion> getPackedVersion(const Object *File, TBDKey Key) {
564 const Array *Versions = File->getArray(K: Keys[Key]);
565 if (!Versions)
566 return PackedVersion(1, 0, 0);
567
568 for (const auto &Val : *Versions) {
569 const auto *Obj = Val.getAsObject();
570 if (!Obj)
571 return make_error<JSONStubError>(Args: getParseErrorMsg(Key));
572
573 auto ValidatePV = [](StringRef Version) -> std::optional<PackedVersion> {
574 PackedVersion PV;
575 auto [success, truncated] = PV.parse64(Str: Version);
576 if (!success || truncated)
577 return std::nullopt;
578 return PV;
579 };
580 // TODO: Take first for now.
581 return getRequiredValue<StringRef, PackedVersion>(
582 Key: TBDKey::Version, Obj, GetValue: &Object::getString, DefaultValue: PackedVersion(1, 0, 0),
583 Validate: ValidatePV);
584 }
585
586 return PackedVersion(1, 0, 0);
587}
588
589Expected<TBDFlags> getFlags(const Object *File) {
590 TBDFlags Flags = TBDFlags::None;
591 const Array *Section = File->getArray(K: Keys[TBDKey::Flags]);
592 if (!Section || Section->empty())
593 return Flags;
594
595 for (auto &Val : *Section) {
596 // FIXME: Flags currently apply to all target triples.
597 const auto *Obj = Val.getAsObject();
598 if (!Obj)
599 return make_error<JSONStubError>(Args: getParseErrorMsg(Key: TBDKey::Flags));
600
601 auto FlagsOrErr =
602 collectFromArray(Key: TBDKey::Attributes, Obj, Append: [&Flags](StringRef Flag) {
603 TBDFlags TBDFlag =
604 StringSwitch<TBDFlags>(Flag)
605 .Case(S: "flat_namespace", Value: TBDFlags::FlatNamespace)
606 .Case(S: "not_app_extension_safe",
607 Value: TBDFlags::NotApplicationExtensionSafe)
608 .Case(S: "sim_support", Value: TBDFlags::SimulatorSupport)
609 .Case(S: "not_for_dyld_shared_cache",
610 Value: TBDFlags::OSLibNotForSharedCache)
611 .Default(Value: TBDFlags::None);
612 Flags |= TBDFlag;
613 });
614
615 if (FlagsOrErr)
616 return std::move(FlagsOrErr);
617
618 return Flags;
619 }
620
621 return Flags;
622}
623
624using IFPtr = std::unique_ptr<InterfaceFile>;
625Expected<IFPtr> parseToInterfaceFile(const Object *File) {
626 auto TargetsOrErr = getTargetsSection(Section: File);
627 if (!TargetsOrErr)
628 return TargetsOrErr.takeError();
629 TargetList Targets = *TargetsOrErr;
630
631 auto NameOrErr = getNameSection(File);
632 if (!NameOrErr)
633 return NameOrErr.takeError();
634 StringRef Name = *NameOrErr;
635
636 auto CurrVersionOrErr = getPackedVersion(File, Key: TBDKey::CurrentVersion);
637 if (!CurrVersionOrErr)
638 return CurrVersionOrErr.takeError();
639 PackedVersion CurrVersion = *CurrVersionOrErr;
640
641 auto CompVersionOrErr = getPackedVersion(File, Key: TBDKey::CompatibilityVersion);
642 if (!CompVersionOrErr)
643 return CompVersionOrErr.takeError();
644 PackedVersion CompVersion = *CompVersionOrErr;
645
646 auto SwiftABIOrErr = getSwiftVersion(File);
647 if (!SwiftABIOrErr)
648 return SwiftABIOrErr.takeError();
649 uint8_t SwiftABI = *SwiftABIOrErr;
650
651 auto FlagsOrErr = getFlags(File);
652 if (!FlagsOrErr)
653 return FlagsOrErr.takeError();
654 TBDFlags Flags = *FlagsOrErr;
655
656 auto UmbrellasOrErr = getUmbrellaSection(File, Targets);
657 if (!UmbrellasOrErr)
658 return UmbrellasOrErr.takeError();
659 const AttrToTargets &Umbrellas = *UmbrellasOrErr;
660
661 auto ClientsOrErr =
662 getLibSection(File, Key: TBDKey::AllowableClients, SubKey: TBDKey::Clients, Targets);
663 if (!ClientsOrErr)
664 return ClientsOrErr.takeError();
665 const AttrToTargets &Clients = *ClientsOrErr;
666
667 auto RLOrErr =
668 getLibSection(File, Key: TBDKey::ReexportLibs, SubKey: TBDKey::Names, Targets);
669 if (!RLOrErr)
670 return RLOrErr.takeError();
671 AttrToTargets ReexportLibs = std::move(*RLOrErr);
672
673 auto RPathsOrErr = getLibSection<InOrderAttrToTargets>(
674 File, Key: TBDKey::RPath, SubKey: TBDKey::Paths, Targets);
675 if (!RPathsOrErr)
676 return RPathsOrErr.takeError();
677 InOrderAttrToTargets RPaths = std::move(*RPathsOrErr);
678
679 auto ExportsOrErr = getSymbolSection(File, Key: TBDKey::Exports, Targets);
680 if (!ExportsOrErr)
681 return ExportsOrErr.takeError();
682 TargetsToSymbols Exports = std::move(*ExportsOrErr);
683
684 auto ReexportsOrErr = getSymbolSection(File, Key: TBDKey::Reexports, Targets);
685 if (!ReexportsOrErr)
686 return ReexportsOrErr.takeError();
687 TargetsToSymbols Reexports = std::move(*ReexportsOrErr);
688
689 auto UndefinedsOrErr = getSymbolSection(File, Key: TBDKey::Undefineds, Targets);
690 if (!UndefinedsOrErr)
691 return UndefinedsOrErr.takeError();
692 TargetsToSymbols Undefineds = std::move(*UndefinedsOrErr);
693
694 IFPtr F(new InterfaceFile);
695 F->setInstallName(Name);
696 F->setCurrentVersion(CurrVersion);
697 F->setCompatibilityVersion(CompVersion);
698 F->setSwiftABIVersion(SwiftABI);
699 F->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
700 F->setApplicationExtensionSafe(
701 !(Flags & TBDFlags::NotApplicationExtensionSafe));
702 F->setSimulatorSupport((Flags & TBDFlags::SimulatorSupport));
703 F->setOSLibNotForSharedCache((Flags & TBDFlags::OSLibNotForSharedCache));
704 for (auto &T : Targets)
705 F->addTarget(Target: T);
706 for (auto &[Lib, Targets] : Clients)
707 for (auto Target : Targets)
708 F->addAllowableClient(InstallName: Lib, Target);
709 for (auto &[Lib, Targets] : ReexportLibs)
710 for (auto Target : Targets)
711 F->addReexportedLibrary(InstallName: Lib, Target);
712 for (auto &[Lib, Targets] : Umbrellas)
713 for (auto Target : Targets)
714 F->addParentUmbrella(Target_: Target, Parent: Lib);
715 for (auto &[Path, Targets] : RPaths)
716 for (auto Target : Targets)
717 F->addRPath(RPath: Path, InputTarget: Target);
718 for (auto &[Targets, Symbols] : Exports) {
719 if (Targets.empty())
720 continue;
721 for (auto &Sym : Symbols)
722 F->addSymbol(Kind: Sym.Kind, Name: Sym.Name, Targets, Flags: Sym.Flags);
723 }
724 for (auto &[Targets, Symbols] : Reexports) {
725 if (Targets.empty())
726 continue;
727 for (auto &Sym : Symbols)
728 F->addSymbol(Kind: Sym.Kind, Name: Sym.Name, Targets, Flags: Sym.Flags);
729 }
730 for (auto &[Targets, Symbols] : Undefineds) {
731 if (Targets.empty())
732 continue;
733 for (auto &Sym : Symbols)
734 F->addSymbol(Kind: Sym.Kind, Name: Sym.Name, Targets, Flags: Sym.Flags);
735 }
736
737 return std::move(F);
738}
739
740Expected<std::vector<IFPtr>> getInlinedLibs(const Object *File) {
741 std::vector<IFPtr> IFs;
742 const Array *Files = File->getArray(K: Keys[TBDKey::Documents]);
743 if (!Files)
744 return std::move(IFs);
745
746 for (auto Lib : *Files) {
747 auto IFOrErr = parseToInterfaceFile(File: Lib.getAsObject());
748 if (!IFOrErr)
749 return IFOrErr.takeError();
750 auto IF = std::move(*IFOrErr);
751 IFs.emplace_back(args: std::move(IF));
752 }
753 return std::move(IFs);
754}
755
756} // namespace StubParser
757} // namespace
758
759Expected<std::unique_ptr<InterfaceFile>>
760MachO::getInterfaceFileFromJSON(StringRef JSON) {
761 auto ValOrErr = parse(JSON);
762 if (!ValOrErr)
763 return ValOrErr.takeError();
764
765 auto *Root = ValOrErr->getAsObject();
766 auto VersionOrErr = StubParser::getVersion(File: Root);
767 if (!VersionOrErr)
768 return VersionOrErr.takeError();
769 FileType Version = *VersionOrErr;
770
771 Object *MainLib = Root->getObject(K: Keys[TBDKey::MainLibrary]);
772 auto IFOrErr = StubParser::parseToInterfaceFile(File: MainLib);
773 if (!IFOrErr)
774 return IFOrErr.takeError();
775 (*IFOrErr)->setFileType(Version);
776 std::unique_ptr<InterfaceFile> IF(std::move(*IFOrErr));
777
778 auto IFsOrErr = StubParser::getInlinedLibs(File: Root);
779 if (!IFsOrErr)
780 return IFsOrErr.takeError();
781 for (auto &File : *IFsOrErr) {
782 File->setFileType(Version);
783 IF->addDocument(Document: std::shared_ptr<InterfaceFile>(std::move(File)));
784 }
785 return std::move(IF);
786}
787
788namespace {
789
790template <typename ContainerT = Array>
791bool insertNonEmptyValues(Object &Obj, TBDKey Key, ContainerT &&Contents) {
792 if (Contents.empty())
793 return false;
794 Obj[Keys[Key]] = std::move(Contents);
795 return true;
796}
797
798std::string getFormattedStr(const MachO::Target &Targ) {
799 std::string PlatformStr = Targ.Platform == PLATFORM_MACCATALYST
800 ? "maccatalyst"
801 : getOSAndEnvironmentName(Platform: Targ.Platform);
802 return (getArchitectureName(Arch: Targ.Arch) + "-" + PlatformStr).str();
803}
804
805template <typename AggregateT>
806std::vector<std::string> serializeTargets(const AggregateT Targets,
807 const TargetList &ActiveTargets) {
808 std::vector<std::string> TargetsStr;
809 if (Targets.size() == ActiveTargets.size())
810 return TargetsStr;
811
812 for (const MachO::Target &Target : Targets)
813 TargetsStr.emplace_back(args: getFormattedStr(Targ: Target));
814
815 return TargetsStr;
816}
817
818Array serializeTargetInfo(const TargetList &ActiveTargets) {
819 Array Targets;
820 for (const auto Targ : ActiveTargets) {
821 Object TargetInfo;
822 if (!Targ.MinDeployment.empty())
823 TargetInfo[Keys[TBDKey::Deployment]] = Targ.MinDeployment.getAsString();
824 TargetInfo[Keys[TBDKey::Target]] = getFormattedStr(Targ);
825 Targets.emplace_back(A: std::move(TargetInfo));
826 }
827 return Targets;
828}
829
830template <typename ValueT, typename EntryT = ValueT>
831Array serializeScalar(TBDKey Key, ValueT Value, ValueT Default = ValueT()) {
832 if (Value == Default)
833 return {};
834 Array Container;
835 Object ScalarObj({Object::KV({Keys[Key], EntryT(Value)})});
836
837 Container.emplace_back(A: std::move(ScalarObj));
838 return Container;
839}
840
841using TargetsToValuesMap =
842 std::map<std::vector<std::string>, std::vector<std::string>>;
843
844template <typename AggregateT = TargetsToValuesMap>
845Array serializeAttrToTargets(AggregateT &Entries, TBDKey Key) {
846 Array Container;
847 for (const auto &[Targets, Values] : Entries) {
848 Object Obj;
849 insertNonEmptyValues(Obj, TBDKey::Targets, std::move(Targets));
850 Obj[Keys[Key]] = Values;
851 Container.emplace_back(A: std::move(Obj));
852 }
853 return Container;
854}
855
856/// When there is no significance in order, the common case, serialize all
857/// attributes in a stable order.
858template <typename ValueT = std::string,
859 typename AggregateT = std::vector<std::pair<MachO::Target, ValueT>>>
860Array serializeField(TBDKey Key, const AggregateT &Values,
861 const TargetList &ActiveTargets, bool IsArray = true) {
862 std::map<ValueT, std::set<MachO::Target>> Entries;
863 for (const auto &[Target, Val] : Values)
864 Entries[Val].insert(Target);
865
866 if (!IsArray) {
867 std::map<std::vector<std::string>, std::string> FinalEntries;
868 for (const auto &[Val, Targets] : Entries)
869 FinalEntries[serializeTargets(Targets, ActiveTargets)] = Val;
870 return serializeAttrToTargets(Entries&: FinalEntries, Key);
871 }
872
873 TargetsToValuesMap FinalEntries;
874 for (const auto &[Val, Targets] : Entries)
875 FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(Val);
876 return serializeAttrToTargets(Entries&: FinalEntries, Key);
877}
878
879Array serializeField(TBDKey Key, const std::vector<InterfaceFileRef> &Values,
880 const TargetList &ActiveTargets) {
881 TargetsToValuesMap FinalEntries;
882 for (const auto &Ref : Values) {
883 TargetList Targets{Ref.targets().begin(), Ref.targets().end()};
884 FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(
885 args: Ref.getInstallName());
886 }
887 return serializeAttrToTargets(Entries&: FinalEntries, Key);
888}
889
890template <
891 typename AggregateT = std::vector<std::pair<MachO::Target, std::string>>>
892Array serializeFieldInInsertionOrder(TBDKey Key, const AggregateT &Values,
893 const TargetList &ActiveTargets) {
894 MapVector<StringRef, std::set<MachO::Target>> Entries;
895 for (const auto &[Target, Val] : Values)
896 Entries[Val].insert(Target);
897
898 TargetsToValuesMap FinalEntries;
899 for (const auto &[Val, Targets] : Entries)
900 FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(
901 args: Val.str());
902 return serializeAttrToTargets(Entries&: FinalEntries, Key);
903}
904
905struct SymbolFields {
906 struct SymbolTypes {
907 std::vector<StringRef> Weaks;
908 std::vector<StringRef> Globals;
909 std::vector<StringRef> TLV;
910 std::vector<StringRef> ObjCClasses;
911 std::vector<StringRef> IVars;
912 std::vector<StringRef> EHTypes;
913
914 bool empty() const {
915 return Weaks.empty() && Globals.empty() && TLV.empty() &&
916 ObjCClasses.empty() && IVars.empty() && EHTypes.empty();
917 }
918 };
919 SymbolTypes Data;
920 SymbolTypes Text;
921};
922
923Array serializeSymbols(InterfaceFile::const_filtered_symbol_range Symbols,
924 const TargetList &ActiveTargets) {
925 auto AssignForSymbolType = [](SymbolFields::SymbolTypes &Assignment,
926 const Symbol *Sym) {
927 switch (Sym->getKind()) {
928 case EncodeKind::ObjectiveCClass:
929 Assignment.ObjCClasses.emplace_back(args: Sym->getName());
930 return;
931 case EncodeKind::ObjectiveCClassEHType:
932 Assignment.EHTypes.emplace_back(args: Sym->getName());
933 return;
934 case EncodeKind::ObjectiveCInstanceVariable:
935 Assignment.IVars.emplace_back(args: Sym->getName());
936 return;
937 case EncodeKind::GlobalSymbol: {
938 if (Sym->isWeakReferenced() || Sym->isWeakDefined())
939 Assignment.Weaks.emplace_back(args: Sym->getName());
940 else if (Sym->isThreadLocalValue())
941 Assignment.TLV.emplace_back(args: Sym->getName());
942 else
943 Assignment.Globals.emplace_back(args: Sym->getName());
944 return;
945 }
946 }
947 };
948
949 std::map<std::vector<std::string>, SymbolFields> Entries;
950 for (const auto *Sym : Symbols) {
951 std::set<MachO::Target> Targets{Sym->targets().begin(),
952 Sym->targets().end()};
953 auto JSONTargets = serializeTargets(Targets, ActiveTargets);
954 if (Sym->isData())
955 AssignForSymbolType(Entries[std::move(JSONTargets)].Data, Sym);
956 else if (Sym->isText())
957 AssignForSymbolType(Entries[std::move(JSONTargets)].Text, Sym);
958 else
959 llvm_unreachable("unexpected symbol type");
960 }
961
962 auto InsertSymbolsToJSON = [](Object &SymSection, TBDKey SegmentKey,
963 SymbolFields::SymbolTypes &SymField) {
964 if (SymField.empty())
965 return;
966 llvm::sort(C&: SymField.Globals);
967 llvm::sort(C&: SymField.TLV);
968 llvm::sort(C&: SymField.Weaks);
969 llvm::sort(C&: SymField.ObjCClasses);
970 llvm::sort(C&: SymField.EHTypes);
971 llvm::sort(C&: SymField.IVars);
972 Object Segment;
973 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::Globals, Contents: std::move(SymField.Globals));
974 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::ThreadLocal, Contents: std::move(SymField.TLV));
975 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::Weak, Contents: std::move(SymField.Weaks));
976 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::ObjCClass,
977 Contents: std::move(SymField.ObjCClasses));
978 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::ObjCEHType,
979 Contents: std::move(SymField.EHTypes));
980 insertNonEmptyValues(Obj&: Segment, Key: TBDKey::ObjCIvar, Contents: std::move(SymField.IVars));
981 insertNonEmptyValues(Obj&: SymSection, Key: SegmentKey, Contents: std::move(Segment));
982 };
983
984 Array SymbolSection;
985 for (auto &[Targets, Fields] : Entries) {
986 Object AllSyms;
987 insertNonEmptyValues(Obj&: AllSyms, Key: TBDKey::Targets, Contents: std::move(Targets));
988 InsertSymbolsToJSON(AllSyms, TBDKey::Data, Fields.Data);
989 InsertSymbolsToJSON(AllSyms, TBDKey::Text, Fields.Text);
990 SymbolSection.emplace_back(A: std::move(AllSyms));
991 }
992
993 return SymbolSection;
994}
995
996Array serializeFlags(const InterfaceFile *File) {
997 // TODO: Give all Targets the same flags for now.
998 Array Flags;
999 if (!File->isTwoLevelNamespace())
1000 Flags.emplace_back(A: "flat_namespace");
1001 if (!File->isApplicationExtensionSafe())
1002 Flags.emplace_back(A: "not_app_extension_safe");
1003 if (File->hasSimulatorSupport())
1004 Flags.emplace_back(A: "sim_support");
1005 if (File->isOSLibNotForSharedCache())
1006 Flags.emplace_back(A: "not_for_dyld_shared_cache");
1007 return serializeScalar(Key: TBDKey::Attributes, Value: std::move(Flags));
1008}
1009
1010Expected<Object> serializeIF(const InterfaceFile *File) {
1011 Object Library;
1012
1013 // Handle required keys.
1014 TargetList ActiveTargets{File->targets().begin(), File->targets().end()};
1015 if (!insertNonEmptyValues(Obj&: Library, Key: TBDKey::TargetInfo,
1016 Contents: serializeTargetInfo(ActiveTargets)))
1017 return make_error<JSONStubError>(Args: getSerializeErrorMsg(Key: TBDKey::TargetInfo));
1018
1019 Array Name = serializeScalar<StringRef>(Key: TBDKey::Name, Value: File->getInstallName());
1020 if (!insertNonEmptyValues(Obj&: Library, Key: TBDKey::InstallName, Contents: std::move(Name)))
1021 return make_error<JSONStubError>(Args: getSerializeErrorMsg(Key: TBDKey::InstallName));
1022
1023 // Handle optional keys.
1024 Array Flags = serializeFlags(File);
1025 insertNonEmptyValues(Obj&: Library, Key: TBDKey::Flags, Contents: std::move(Flags));
1026
1027 Array CurrentV = serializeScalar<PackedVersion, std::string>(
1028 Key: TBDKey::Version, Value: File->getCurrentVersion(), Default: PackedVersion(1, 0, 0));
1029 insertNonEmptyValues(Obj&: Library, Key: TBDKey::CurrentVersion, Contents: std::move(CurrentV));
1030
1031 Array CompatV = serializeScalar<PackedVersion, std::string>(
1032 Key: TBDKey::Version, Value: File->getCompatibilityVersion(), Default: PackedVersion(1, 0, 0));
1033 insertNonEmptyValues(Obj&: Library, Key: TBDKey::CompatibilityVersion,
1034 Contents: std::move(CompatV));
1035
1036 Array SwiftABI = serializeScalar<uint8_t, int64_t>(
1037 Key: TBDKey::ABI, Value: File->getSwiftABIVersion(), Default: 0u);
1038 insertNonEmptyValues(Obj&: Library, Key: TBDKey::SwiftABI, Contents: std::move(SwiftABI));
1039
1040 Array RPaths = serializeFieldInInsertionOrder(Key: TBDKey::Paths, Values: File->rpaths(),
1041 ActiveTargets);
1042 insertNonEmptyValues(Obj&: Library, Key: TBDKey::RPath, Contents: std::move(RPaths));
1043
1044 Array Umbrellas = serializeField(Key: TBDKey::Umbrella, Values: File->umbrellas(),
1045 ActiveTargets, /*IsArray=*/false);
1046 insertNonEmptyValues(Obj&: Library, Key: TBDKey::ParentUmbrella, Contents: std::move(Umbrellas));
1047
1048 Array Clients =
1049 serializeField(Key: TBDKey::Clients, Values: File->allowableClients(), ActiveTargets);
1050 insertNonEmptyValues(Obj&: Library, Key: TBDKey::AllowableClients, Contents: std::move(Clients));
1051
1052 Array ReexportLibs =
1053 serializeField(Key: TBDKey::Names, Values: File->reexportedLibraries(), ActiveTargets);
1054 insertNonEmptyValues(Obj&: Library, Key: TBDKey::ReexportLibs, Contents: std::move(ReexportLibs));
1055
1056 // Handle symbols.
1057 Array Exports = serializeSymbols(Symbols: File->exports(), ActiveTargets);
1058 insertNonEmptyValues(Obj&: Library, Key: TBDKey::Exports, Contents: std::move(Exports));
1059
1060 Array Reexports = serializeSymbols(Symbols: File->reexports(), ActiveTargets);
1061 insertNonEmptyValues(Obj&: Library, Key: TBDKey::Reexports, Contents: std::move(Reexports));
1062
1063 if (!File->isTwoLevelNamespace()) {
1064 Array Undefineds = serializeSymbols(Symbols: File->undefineds(), ActiveTargets);
1065 insertNonEmptyValues(Obj&: Library, Key: TBDKey::Undefineds, Contents: std::move(Undefineds));
1066 }
1067
1068 return std::move(Library);
1069}
1070
1071Expected<Object> getJSON(const InterfaceFile *File, const FileType FileKind) {
1072 assert(FileKind == FileType::TBD_V5 && "unexpected json file format version");
1073 Object Root;
1074
1075 auto MainLibOrErr = serializeIF(File);
1076 if (!MainLibOrErr)
1077 return MainLibOrErr;
1078 Root[Keys[TBDKey::MainLibrary]] = std::move(*MainLibOrErr);
1079 Array Documents;
1080 for (const auto &Doc : File->documents()) {
1081 auto LibOrErr = serializeIF(File: Doc.get());
1082 if (!LibOrErr)
1083 return LibOrErr;
1084 Documents.emplace_back(A: std::move(*LibOrErr));
1085 }
1086
1087 Root[Keys[TBDKey::TBDVersion]] = 5;
1088 insertNonEmptyValues(Obj&: Root, Key: TBDKey::Documents, Contents: std::move(Documents));
1089 return std::move(Root);
1090}
1091
1092} // namespace
1093
1094Error MachO::serializeInterfaceFileToJSON(raw_ostream &OS,
1095 const InterfaceFile &File,
1096 const FileType FileKind,
1097 bool Compact) {
1098 auto TextFile = getJSON(File: &File, FileKind);
1099 if (!TextFile)
1100 return TextFile.takeError();
1101 if (Compact)
1102 OS << formatv(Fmt: "{0}", Vals: Value(std::move(*TextFile))) << "\n";
1103 else
1104 OS << formatv(Fmt: "{0:2}", Vals: Value(std::move(*TextFile))) << "\n";
1105 return Error::success();
1106}
1107