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