Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / llvm / lib / TextAPI / TextStubV5.cpp
blobf6a3fef088e46411ea611745e86ba5431f965e77
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.
11 //===----------------------------------------------------------------------===//
12 #include "TextStubCommon.h"
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/Support/JSON.h"
15 #include <utility>
17 // clang-format off
20 JSON Format specification.
22 All library level keys, accept target values and are defaulted if not specified.
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
29 "target": "x86_64-macos",
30 "min_deployment": "10.14" # Optional: minOS defaults to 0
33 "target": "arm64-macos",
34 "min_deployment": "10.14"
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:
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": []
59 "data": { ... }, # List of Data segment symbols
60 }],
61 "reexported_symbols": [{ ... }], # List of reexported symbols section
62 "undefined_symbols": [{ ... }] # List of undefined symbols section
64 "libraries": [ # Optional: Array of inlined libraries
65 {...}, {...}, {...}
69 // clang-format on
71 using namespace llvm;
72 using namespace llvm::json;
73 using namespace llvm::MachO;
75 namespace {
76 struct JSONSymbol {
77 SymbolKind Kind;
78 std::string Name;
79 SymbolFlags Flags;
82 using AttrToTargets = std::map<std::string, TargetList>;
83 using TargetsToSymbols =
84 SmallVector<std::pair<TargetList, std::vector<JSONSymbol>>>;
86 enum TBDKey : size_t {
87 TBDVersion = 0U,
88 MainLibrary,
89 Documents,
90 TargetInfo,
91 Targets,
92 Target,
93 Deployment,
94 Flags,
95 Attributes,
96 InstallName,
97 CurrentVersion,
98 CompatibilityVersion,
99 Version,
100 SwiftABI,
101 ABI,
102 ParentUmbrella,
103 Umbrella,
104 AllowableClients,
105 Clients,
106 ReexportLibs,
107 Names,
108 Name,
109 Exports,
110 Reexports,
111 Undefineds,
112 Data,
113 Text,
114 Weak,
115 ThreadLocal,
116 Globals,
117 ObjCClass,
118 ObjCEHType,
119 ObjCIvar,
120 RPath,
121 Paths,
124 std::array<StringRef, 64> Keys = {
125 "tapi_tbd_version",
126 "main_library",
127 "libraries",
128 "target_info",
129 "targets",
130 "target",
131 "min_deployment",
132 "flags",
133 "attributes",
134 "install_names",
135 "current_versions",
136 "compatibility_versions",
137 "version",
138 "swift_abi",
139 "abi",
140 "parent_umbrellas",
141 "umbrella",
142 "allowable_clients",
143 "clients",
144 "reexported_libraries",
145 "names",
146 "name",
147 "exported_symbols",
148 "reexported_symbols",
149 "undefined_symbols",
150 "data",
151 "text",
152 "weak",
153 "thread_local",
154 "global",
155 "objc_class",
156 "objc_eh_type",
157 "objc_ivar",
158 "rpaths",
159 "paths",
162 static llvm::SmallString<128> getParseErrorMsg(TBDKey Key) {
163 return {"invalid ", Keys[Key], " section"};
166 static llvm::SmallString<128> getSerializeErrorMsg(TBDKey Key) {
167 return {"missing ", Keys[Key], " information"};
170 class JSONStubError : public llvm::ErrorInfo<llvm::json::ParseError> {
171 public:
172 JSONStubError(Twine ErrMsg) : Message(ErrMsg.str()) {}
174 void log(llvm::raw_ostream &OS) const override { OS << Message << "\n"; }
175 std::error_code convertToErrorCode() const override {
176 return llvm::inconvertibleErrorCode();
179 private:
180 std::string Message;
183 template <typename JsonT, typename StubT = JsonT>
184 Expected<StubT> getRequiredValue(
185 TBDKey Key, const Object *Obj,
186 std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
187 std::function<std::optional<StubT>(JsonT)> Validate = nullptr) {
188 std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
189 if (!Val)
190 return make_error<JSONStubError>(getParseErrorMsg(Key));
192 if (Validate == nullptr)
193 return static_cast<StubT>(*Val);
195 std::optional<StubT> Result = Validate(*Val);
196 if (!Result.has_value())
197 return make_error<JSONStubError>(getParseErrorMsg(Key));
198 return Result.value();
201 template <typename JsonT, typename StubT = JsonT>
202 Expected<StubT> getRequiredValue(
203 TBDKey Key, const Object *Obj,
204 std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue,
205 StubT DefaultValue, std::function<std::optional<StubT>(JsonT)> Validate) {
206 std::optional<JsonT> Val = GetValue(Obj, Keys[Key]);
207 if (!Val)
208 return DefaultValue;
210 std::optional<StubT> Result;
211 Result = Validate(*Val);
212 if (!Result.has_value())
213 return make_error<JSONStubError>(getParseErrorMsg(Key));
214 return Result.value();
217 Error collectFromArray(TBDKey Key, const Object *Obj,
218 std::function<void(StringRef)> Append,
219 bool IsRequired = false) {
220 const auto *Values = Obj->getArray(Keys[Key]);
221 if (!Values) {
222 if (IsRequired)
223 return make_error<JSONStubError>(getParseErrorMsg(Key));
224 return Error::success();
227 for (const Value &Val : *Values) {
228 auto ValStr = Val.getAsString();
229 if (!ValStr.has_value())
230 return make_error<JSONStubError>(getParseErrorMsg(Key));
231 Append(ValStr.value());
234 return Error::success();
237 namespace StubParser {
239 Expected<FileType> getVersion(const Object *File) {
240 auto VersionOrErr = getRequiredValue<int64_t, FileType>(
241 TBDKey::TBDVersion, File, &Object::getInteger,
242 [](int64_t Val) -> std::optional<FileType> {
243 unsigned Result = Val;
244 if (Result != 5)
245 return std::nullopt;
246 return FileType::TBD_V5;
249 if (!VersionOrErr)
250 return VersionOrErr.takeError();
251 return *VersionOrErr;
254 Expected<TargetList> getTargets(const Object *Section) {
255 const auto *Targets = Section->getArray(Keys[TBDKey::Targets]);
256 if (!Targets)
257 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
259 TargetList IFTargets;
260 for (const Value &JSONTarget : *Targets) {
261 auto TargetStr = JSONTarget.getAsString();
262 if (!TargetStr.has_value())
263 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
264 auto TargetOrErr = Target::create(TargetStr.value());
265 if (!TargetOrErr)
266 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
267 IFTargets.push_back(*TargetOrErr);
269 return std::move(IFTargets);
272 Expected<TargetList> getTargetsSection(const Object *Section) {
273 const Array *Targets = Section->getArray(Keys[TBDKey::TargetInfo]);
274 if (!Targets)
275 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Targets));
277 TargetList IFTargets;
278 for (const Value &JSONTarget : *Targets) {
279 const auto *Obj = JSONTarget.getAsObject();
280 if (!Obj)
281 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
282 auto TargetStr =
283 getRequiredValue<StringRef>(TBDKey::Target, Obj, &Object::getString);
284 if (!TargetStr)
285 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
286 auto TargetOrErr = Target::create(*TargetStr);
287 if (!TargetOrErr)
288 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Target));
290 auto VersionStr = Obj->getString(Keys[TBDKey::Deployment]);
291 VersionTuple Version;
292 if (VersionStr && Version.tryParse(*VersionStr))
293 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Deployment));
294 TargetOrErr->MinDeployment = Version;
296 // Convert to LLVM::Triple to accurately compute minOS + platform + arch
297 // pairing.
298 IFTargets.push_back(
299 MachO::Target(Triple(getTargetTripleName(*TargetOrErr))));
301 return std::move(IFTargets);
304 Error collectSymbolsFromSegment(const Object *Segment, TargetsToSymbols &Result,
305 SymbolFlags SectionFlag) {
306 auto Err = collectFromArray(
307 TBDKey::Globals, Segment, [&Result, &SectionFlag](StringRef Name) {
308 JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(), SectionFlag};
309 Result.back().second.emplace_back(Sym);
311 if (Err)
312 return Err;
314 Err = collectFromArray(
315 TBDKey::ObjCClass, Segment, [&Result, &SectionFlag](StringRef Name) {
316 JSONSymbol Sym = {SymbolKind::ObjectiveCClass, Name.str(), SectionFlag};
317 Result.back().second.emplace_back(Sym);
319 if (Err)
320 return Err;
322 Err = collectFromArray(TBDKey::ObjCEHType, Segment,
323 [&Result, &SectionFlag](StringRef Name) {
324 JSONSymbol Sym = {SymbolKind::ObjectiveCClassEHType,
325 Name.str(), SectionFlag};
326 Result.back().second.emplace_back(Sym);
328 if (Err)
329 return Err;
331 Err = collectFromArray(
332 TBDKey::ObjCIvar, Segment, [&Result, &SectionFlag](StringRef Name) {
333 JSONSymbol Sym = {SymbolKind::ObjectiveCInstanceVariable, Name.str(),
334 SectionFlag};
335 Result.back().second.emplace_back(Sym);
337 if (Err)
338 return Err;
340 SymbolFlags WeakFlag =
341 SectionFlag |
342 (((SectionFlag & SymbolFlags::Undefined) == SymbolFlags::Undefined)
343 ? SymbolFlags::WeakReferenced
344 : SymbolFlags::WeakDefined);
345 Err = collectFromArray(
346 TBDKey::Weak, Segment, [&Result, WeakFlag](StringRef Name) {
347 JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(), WeakFlag};
348 Result.back().second.emplace_back(Sym);
350 if (Err)
351 return Err;
353 Err = collectFromArray(
354 TBDKey::ThreadLocal, Segment, [&Result, SectionFlag](StringRef Name) {
355 JSONSymbol Sym = {SymbolKind::GlobalSymbol, Name.str(),
356 SymbolFlags::ThreadLocalValue | SectionFlag};
357 Result.back().second.emplace_back(Sym);
359 if (Err)
360 return Err;
362 return Error::success();
365 Expected<StringRef> getNameSection(const Object *File) {
366 const Array *Section = File->getArray(Keys[TBDKey::InstallName]);
367 if (!Section)
368 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
370 assert(!Section->empty() && "unexpected missing install name");
371 // TODO: Just take first for now.
372 const auto *Obj = Section->front().getAsObject();
373 if (!Obj)
374 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::InstallName));
376 return getRequiredValue<StringRef>(TBDKey::Name, Obj, &Object::getString);
379 Expected<TargetsToSymbols> getSymbolSection(const Object *File, TBDKey Key,
380 TargetList &Targets) {
382 const Array *Section = File->getArray(Keys[Key]);
383 if (!Section)
384 return TargetsToSymbols();
386 SymbolFlags SectionFlag;
387 switch (Key) {
388 case TBDKey::Reexports:
389 SectionFlag = SymbolFlags::Rexported;
390 break;
391 case TBDKey::Undefineds:
392 SectionFlag = SymbolFlags::Undefined;
393 break;
394 default:
395 SectionFlag = SymbolFlags::None;
396 break;
399 TargetsToSymbols Result;
400 TargetList MappedTargets;
401 for (auto Val : *Section) {
402 auto *Obj = Val.getAsObject();
403 if (!Obj)
404 continue;
406 auto TargetsOrErr = getTargets(Obj);
407 if (!TargetsOrErr) {
408 MappedTargets = Targets;
409 consumeError(TargetsOrErr.takeError());
410 } else {
411 MappedTargets = *TargetsOrErr;
413 Result.emplace_back(
414 std::make_pair(std::move(MappedTargets), std::vector<JSONSymbol>()));
416 auto *DataSection = Obj->getObject(Keys[TBDKey::Data]);
417 auto *TextSection = Obj->getObject(Keys[TBDKey::Text]);
418 // There should be at least one valid section.
419 if (!DataSection && !TextSection)
420 return make_error<JSONStubError>(getParseErrorMsg(Key));
422 if (DataSection) {
423 auto Err = collectSymbolsFromSegment(DataSection, Result,
424 SectionFlag | SymbolFlags::Data);
425 if (Err)
426 return std::move(Err);
428 if (TextSection) {
429 auto Err = collectSymbolsFromSegment(TextSection, Result,
430 SectionFlag | SymbolFlags::Text);
431 if (Err)
432 return std::move(Err);
436 return std::move(Result);
439 Expected<AttrToTargets> getLibSection(const Object *File, TBDKey Key,
440 TBDKey SubKey,
441 const TargetList &Targets) {
442 auto *Section = File->getArray(Keys[Key]);
443 if (!Section)
444 return AttrToTargets();
446 AttrToTargets Result;
447 TargetList MappedTargets;
448 for (auto Val : *Section) {
449 auto *Obj = Val.getAsObject();
450 if (!Obj)
451 continue;
453 auto TargetsOrErr = getTargets(Obj);
454 if (!TargetsOrErr) {
455 MappedTargets = Targets;
456 consumeError(TargetsOrErr.takeError());
457 } else {
458 MappedTargets = *TargetsOrErr;
460 auto Err =
461 collectFromArray(SubKey, Obj, [&Result, &MappedTargets](StringRef Key) {
462 Result[Key.str()] = MappedTargets;
464 if (Err)
465 return std::move(Err);
468 return std::move(Result);
471 Expected<AttrToTargets> getUmbrellaSection(const Object *File,
472 const TargetList &Targets) {
473 const auto *Umbrella = File->getArray(Keys[TBDKey::ParentUmbrella]);
474 if (!Umbrella)
475 return AttrToTargets();
477 AttrToTargets Result;
478 TargetList MappedTargets;
479 for (auto Val : *Umbrella) {
480 auto *Obj = Val.getAsObject();
481 if (!Obj)
482 return make_error<JSONStubError>(
483 getParseErrorMsg(TBDKey::ParentUmbrella));
485 // Get Targets section.
486 auto TargetsOrErr = getTargets(Obj);
487 if (!TargetsOrErr) {
488 MappedTargets = Targets;
489 consumeError(TargetsOrErr.takeError());
490 } else {
491 MappedTargets = *TargetsOrErr;
494 auto UmbrellaOrErr =
495 getRequiredValue<StringRef>(TBDKey::Umbrella, Obj, &Object::getString);
496 if (!UmbrellaOrErr)
497 return UmbrellaOrErr.takeError();
498 Result[UmbrellaOrErr->str()] = Targets;
500 return std::move(Result);
503 Expected<uint8_t> getSwiftVersion(const Object *File) {
504 const Array *Versions = File->getArray(Keys[TBDKey::SwiftABI]);
505 if (!Versions)
506 return 0;
508 for (const auto &Val : *Versions) {
509 const auto *Obj = Val.getAsObject();
510 if (!Obj)
511 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::SwiftABI));
513 // TODO: Take first for now.
514 return getRequiredValue<int64_t, uint8_t>(TBDKey::ABI, Obj,
515 &Object::getInteger);
518 return 0;
521 Expected<PackedVersion> getPackedVersion(const Object *File, TBDKey Key) {
522 const Array *Versions = File->getArray(Keys[Key]);
523 if (!Versions)
524 return PackedVersion(1, 0, 0);
526 for (const auto &Val : *Versions) {
527 const auto *Obj = Val.getAsObject();
528 if (!Obj)
529 return make_error<JSONStubError>(getParseErrorMsg(Key));
531 auto ValidatePV = [](StringRef Version) -> std::optional<PackedVersion> {
532 PackedVersion PV;
533 auto [success, truncated] = PV.parse64(Version);
534 if (!success || truncated)
535 return std::nullopt;
536 return PV;
538 // TODO: Take first for now.
539 return getRequiredValue<StringRef, PackedVersion>(
540 TBDKey::Version, Obj, &Object::getString, PackedVersion(1, 0, 0),
541 ValidatePV);
544 return PackedVersion(1, 0, 0);
547 Expected<TBDFlags> getFlags(const Object *File) {
548 TBDFlags Flags = TBDFlags::None;
549 const Array *Section = File->getArray(Keys[TBDKey::Flags]);
550 if (!Section || Section->empty())
551 return Flags;
553 for (auto &Val : *Section) {
554 // FIXME: Flags currently apply to all target triples.
555 const auto *Obj = Val.getAsObject();
556 if (!Obj)
557 return make_error<JSONStubError>(getParseErrorMsg(TBDKey::Flags));
559 auto FlagsOrErr =
560 collectFromArray(TBDKey::Attributes, Obj, [&Flags](StringRef Flag) {
561 TBDFlags TBDFlag =
562 StringSwitch<TBDFlags>(Flag)
563 .Case("flat_namespace", TBDFlags::FlatNamespace)
564 .Case("not_app_extension_safe",
565 TBDFlags::NotApplicationExtensionSafe)
566 .Case("sim_support", TBDFlags::SimulatorSupport)
567 .Default(TBDFlags::None);
568 Flags |= TBDFlag;
571 if (FlagsOrErr)
572 return std::move(FlagsOrErr);
574 return Flags;
577 return Flags;
580 using IFPtr = std::unique_ptr<InterfaceFile>;
581 Expected<IFPtr> parseToInterfaceFile(const Object *File) {
582 auto TargetsOrErr = getTargetsSection(File);
583 if (!TargetsOrErr)
584 return TargetsOrErr.takeError();
585 TargetList Targets = *TargetsOrErr;
587 auto NameOrErr = getNameSection(File);
588 if (!NameOrErr)
589 return NameOrErr.takeError();
590 StringRef Name = *NameOrErr;
592 auto CurrVersionOrErr = getPackedVersion(File, TBDKey::CurrentVersion);
593 if (!CurrVersionOrErr)
594 return CurrVersionOrErr.takeError();
595 PackedVersion CurrVersion = *CurrVersionOrErr;
597 auto CompVersionOrErr = getPackedVersion(File, TBDKey::CompatibilityVersion);
598 if (!CompVersionOrErr)
599 return CompVersionOrErr.takeError();
600 PackedVersion CompVersion = *CompVersionOrErr;
602 auto SwiftABIOrErr = getSwiftVersion(File);
603 if (!SwiftABIOrErr)
604 return SwiftABIOrErr.takeError();
605 uint8_t SwiftABI = *SwiftABIOrErr;
607 auto FlagsOrErr = getFlags(File);
608 if (!FlagsOrErr)
609 return FlagsOrErr.takeError();
610 TBDFlags Flags = *FlagsOrErr;
612 auto UmbrellasOrErr = getUmbrellaSection(File, Targets);
613 if (!UmbrellasOrErr)
614 return UmbrellasOrErr.takeError();
615 AttrToTargets Umbrellas = *UmbrellasOrErr;
617 auto ClientsOrErr =
618 getLibSection(File, TBDKey::AllowableClients, TBDKey::Clients, Targets);
619 if (!ClientsOrErr)
620 return ClientsOrErr.takeError();
621 AttrToTargets Clients = *ClientsOrErr;
623 auto RLOrErr =
624 getLibSection(File, TBDKey::ReexportLibs, TBDKey::Names, Targets);
625 if (!RLOrErr)
626 return RLOrErr.takeError();
627 AttrToTargets ReexportLibs = std::move(*RLOrErr);
629 auto RPathsOrErr = getLibSection(File, TBDKey::RPath, TBDKey::Paths, Targets);
630 if (!RPathsOrErr)
631 return RPathsOrErr.takeError();
632 AttrToTargets RPaths = std::move(*RPathsOrErr);
634 auto ExportsOrErr = getSymbolSection(File, TBDKey::Exports, Targets);
635 if (!ExportsOrErr)
636 return ExportsOrErr.takeError();
637 TargetsToSymbols Exports = std::move(*ExportsOrErr);
639 auto ReexportsOrErr = getSymbolSection(File, TBDKey::Reexports, Targets);
640 if (!ReexportsOrErr)
641 return ReexportsOrErr.takeError();
642 TargetsToSymbols Reexports = std::move(*ReexportsOrErr);
644 auto UndefinedsOrErr = getSymbolSection(File, TBDKey::Undefineds, Targets);
645 if (!UndefinedsOrErr)
646 return UndefinedsOrErr.takeError();
647 TargetsToSymbols Undefineds = std::move(*UndefinedsOrErr);
649 IFPtr F(new InterfaceFile);
650 F->setInstallName(Name);
651 F->setCurrentVersion(CurrVersion);
652 F->setCompatibilityVersion(CompVersion);
653 F->setSwiftABIVersion(SwiftABI);
654 F->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace));
655 F->setApplicationExtensionSafe(
656 !(Flags & TBDFlags::NotApplicationExtensionSafe));
657 F->setSimulatorSupport((Flags & TBDFlags::SimulatorSupport));
658 for (auto &T : Targets)
659 F->addTarget(T);
660 for (auto &[Lib, Targets] : Clients)
661 for (auto Target : Targets)
662 F->addAllowableClient(Lib, Target);
663 for (auto &[Lib, Targets] : ReexportLibs)
664 for (auto Target : Targets)
665 F->addReexportedLibrary(Lib, Target);
666 for (auto &[Lib, Targets] : Umbrellas)
667 for (auto Target : Targets)
668 F->addParentUmbrella(Target, Lib);
669 for (auto &[Path, Targets] : RPaths)
670 for (auto Target : Targets)
671 F->addRPath(Target, Path);
672 for (auto &[Targets, Symbols] : Exports)
673 for (auto &Sym : Symbols)
674 F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
675 for (auto &[Targets, Symbols] : Reexports)
676 for (auto &Sym : Symbols)
677 F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
678 for (auto &[Targets, Symbols] : Undefineds)
679 for (auto &Sym : Symbols)
680 F->addSymbol(Sym.Kind, Sym.Name, Targets, Sym.Flags);
682 return std::move(F);
685 Expected<std::vector<IFPtr>> getInlinedLibs(const Object *File) {
686 std::vector<IFPtr> IFs;
687 const Array *Files = File->getArray(Keys[TBDKey::Documents]);
688 if (!Files)
689 return std::move(IFs);
691 for (auto Lib : *Files) {
692 auto IFOrErr = parseToInterfaceFile(Lib.getAsObject());
693 if (!IFOrErr)
694 return IFOrErr.takeError();
695 auto IF = std::move(*IFOrErr);
696 IFs.emplace_back(std::move(IF));
698 return std::move(IFs);
701 } // namespace StubParser
702 } // namespace
704 Expected<std::unique_ptr<InterfaceFile>>
705 MachO::getInterfaceFileFromJSON(StringRef JSON) {
706 auto ValOrErr = parse(JSON);
707 if (!ValOrErr)
708 return ValOrErr.takeError();
710 auto *Root = ValOrErr->getAsObject();
711 auto VersionOrErr = StubParser::getVersion(Root);
712 if (!VersionOrErr)
713 return VersionOrErr.takeError();
714 FileType Version = *VersionOrErr;
716 Object *MainLib = Root->getObject(Keys[TBDKey::MainLibrary]);
717 auto IFOrErr = StubParser::parseToInterfaceFile(MainLib);
718 if (!IFOrErr)
719 return IFOrErr.takeError();
720 (*IFOrErr)->setFileType(Version);
721 std::unique_ptr<InterfaceFile> IF(std::move(*IFOrErr));
723 auto IFsOrErr = StubParser::getInlinedLibs(Root);
724 if (!IFsOrErr)
725 return IFsOrErr.takeError();
726 for (auto &File : *IFsOrErr) {
727 File->setFileType(Version);
728 IF->addDocument(std::shared_ptr<InterfaceFile>(std::move(File)));
730 return std::move(IF);
733 namespace {
735 template <typename ContainerT = Array>
736 bool insertNonEmptyValues(Object &Obj, TBDKey Key, ContainerT &&Contents) {
737 if (Contents.empty())
738 return false;
739 Obj[Keys[Key]] = std::move(Contents);
740 return true;
743 std::string getFormattedStr(const MachO::Target &Targ) {
744 std::string PlatformStr = Targ.Platform == PLATFORM_MACCATALYST
745 ? "maccatalyst"
746 : getOSAndEnvironmentName(Targ.Platform);
747 return (getArchitectureName(Targ.Arch) + "-" + PlatformStr).str();
750 template <typename AggregateT>
751 std::vector<std::string> serializeTargets(const AggregateT Targets,
752 const TargetList &ActiveTargets) {
753 std::vector<std::string> TargetsStr;
754 if (Targets.size() == ActiveTargets.size())
755 return TargetsStr;
757 for (const MachO::Target &Target : Targets)
758 TargetsStr.emplace_back(getFormattedStr(Target));
760 return TargetsStr;
763 Array serializeTargetInfo(const TargetList &ActiveTargets) {
764 Array Targets;
765 for (const auto Targ : ActiveTargets) {
766 Object TargetInfo;
767 TargetInfo[Keys[TBDKey::Deployment]] = Targ.MinDeployment.getAsString();
768 TargetInfo[Keys[TBDKey::Target]] = getFormattedStr(Targ);
769 Targets.emplace_back(std::move(TargetInfo));
771 return Targets;
774 template <typename ValueT, typename EntryT = ValueT>
775 Array serializeScalar(TBDKey Key, ValueT Value, ValueT Default = ValueT()) {
776 if (Value == Default)
777 return {};
778 Array Container;
779 Object ScalarObj({Object::KV({Keys[Key], EntryT(Value)})});
781 Container.emplace_back(std::move(ScalarObj));
782 return Container;
785 using TargetsToValuesMap =
786 std::map<std::vector<std::string>, std::vector<std::string>>;
788 template <typename AggregateT = TargetsToValuesMap>
789 Array serializeAttrToTargets(AggregateT &Entries, TBDKey Key) {
790 Array Container;
791 for (const auto &[Targets, Values] : Entries) {
792 Object Obj;
793 insertNonEmptyValues(Obj, TBDKey::Targets, std::move(Targets));
794 Obj[Keys[Key]] = Values;
795 Container.emplace_back(std::move(Obj));
797 return Container;
800 template <typename ValueT = std::string,
801 typename AggregateT = std::vector<std::pair<MachO::Target, ValueT>>>
802 Array serializeField(TBDKey Key, const AggregateT &Values,
803 const TargetList &ActiveTargets, bool IsArray = true) {
804 std::map<ValueT, std::set<MachO::Target>> Entries;
805 for (const auto &[Target, Val] : Values)
806 Entries[Val].insert(Target);
808 if (!IsArray) {
809 std::map<std::vector<std::string>, std::string> FinalEntries;
810 for (const auto &[Val, Targets] : Entries)
811 FinalEntries[serializeTargets(Targets, ActiveTargets)] = Val;
812 return serializeAttrToTargets(FinalEntries, Key);
815 TargetsToValuesMap FinalEntries;
816 for (const auto &[Val, Targets] : Entries)
817 FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(Val);
818 return serializeAttrToTargets(FinalEntries, Key);
821 Array serializeField(TBDKey Key, const std::vector<InterfaceFileRef> &Values,
822 const TargetList &ActiveTargets) {
823 TargetsToValuesMap FinalEntries;
824 for (const auto &Ref : Values) {
825 TargetList Targets{Ref.targets().begin(), Ref.targets().end()};
826 FinalEntries[serializeTargets(Targets, ActiveTargets)].emplace_back(
827 Ref.getInstallName());
829 return serializeAttrToTargets(FinalEntries, Key);
832 struct SymbolFields {
833 struct SymbolTypes {
834 std::vector<StringRef> Weaks;
835 std::vector<StringRef> Globals;
836 std::vector<StringRef> TLV;
837 std::vector<StringRef> ObjCClasses;
838 std::vector<StringRef> IVars;
839 std::vector<StringRef> EHTypes;
841 bool empty() const {
842 return Weaks.empty() && Globals.empty() && TLV.empty() &&
843 ObjCClasses.empty() && IVars.empty() && EHTypes.empty();
846 SymbolTypes Data;
847 SymbolTypes Text;
850 Array serializeSymbols(InterfaceFile::const_filtered_symbol_range Symbols,
851 const TargetList &ActiveTargets) {
852 auto AssignForSymbolType = [](SymbolFields::SymbolTypes &Assignment,
853 const Symbol *Sym) {
854 switch (Sym->getKind()) {
855 case SymbolKind::ObjectiveCClass:
856 Assignment.ObjCClasses.emplace_back(Sym->getName());
857 return;
858 case SymbolKind::ObjectiveCClassEHType:
859 Assignment.EHTypes.emplace_back(Sym->getName());
860 return;
861 case SymbolKind::ObjectiveCInstanceVariable:
862 Assignment.IVars.emplace_back(Sym->getName());
863 return;
864 case SymbolKind::GlobalSymbol: {
865 if (Sym->isWeakReferenced() || Sym->isWeakDefined())
866 Assignment.Weaks.emplace_back(Sym->getName());
867 else if (Sym->isThreadLocalValue())
868 Assignment.TLV.emplace_back(Sym->getName());
869 else
870 Assignment.Globals.emplace_back(Sym->getName());
871 return;
876 std::map<std::vector<std::string>, SymbolFields> Entries;
877 for (const auto *Sym : Symbols) {
878 std::set<MachO::Target> Targets{Sym->targets().begin(),
879 Sym->targets().end()};
880 auto JSONTargets = serializeTargets(Targets, ActiveTargets);
881 if (Sym->isData())
882 AssignForSymbolType(Entries[std::move(JSONTargets)].Data, Sym);
883 else if (Sym->isText())
884 AssignForSymbolType(Entries[std::move(JSONTargets)].Text, Sym);
885 else
886 llvm_unreachable("unexpected symbol type");
889 auto InsertSymbolsToJSON = [](Object &SymSection, TBDKey SegmentKey,
890 SymbolFields::SymbolTypes &SymField) {
891 if (SymField.empty())
892 return;
893 Object Segment;
894 insertNonEmptyValues(Segment, TBDKey::Globals, std::move(SymField.Globals));
895 insertNonEmptyValues(Segment, TBDKey::ThreadLocal, std::move(SymField.TLV));
896 insertNonEmptyValues(Segment, TBDKey::Weak, std::move(SymField.Weaks));
897 insertNonEmptyValues(Segment, TBDKey::ObjCClass,
898 std::move(SymField.ObjCClasses));
899 insertNonEmptyValues(Segment, TBDKey::ObjCEHType,
900 std::move(SymField.EHTypes));
901 insertNonEmptyValues(Segment, TBDKey::ObjCIvar, std::move(SymField.IVars));
902 insertNonEmptyValues(SymSection, SegmentKey, std::move(Segment));
905 Array SymbolSection;
906 for (auto &[Targets, Fields] : Entries) {
907 Object AllSyms;
908 insertNonEmptyValues(AllSyms, TBDKey::Targets, std::move(Targets));
909 InsertSymbolsToJSON(AllSyms, TBDKey::Data, Fields.Data);
910 InsertSymbolsToJSON(AllSyms, TBDKey::Text, Fields.Text);
911 SymbolSection.emplace_back(std::move(AllSyms));
914 return SymbolSection;
917 Array serializeFlags(const InterfaceFile *File) {
918 // TODO: Give all Targets the same flags for now.
919 Array Flags;
920 if (!File->isTwoLevelNamespace())
921 Flags.emplace_back("flat_namespace");
922 if (!File->isApplicationExtensionSafe())
923 Flags.emplace_back("not_app_extension_safe");
924 if (File->hasSimulatorSupport())
925 Flags.emplace_back("sim_support");
926 return serializeScalar(TBDKey::Attributes, std::move(Flags));
929 Expected<Object> serializeIF(const InterfaceFile *File) {
930 Object Library;
932 // Handle required keys.
933 TargetList ActiveTargets{File->targets().begin(), File->targets().end()};
934 if (!insertNonEmptyValues(Library, TBDKey::TargetInfo,
935 serializeTargetInfo(ActiveTargets)))
936 return make_error<JSONStubError>(getSerializeErrorMsg(TBDKey::TargetInfo));
938 Array Name = serializeScalar<StringRef>(TBDKey::Name, File->getInstallName());
939 if (!insertNonEmptyValues(Library, TBDKey::InstallName, std::move(Name)))
940 return make_error<JSONStubError>(getSerializeErrorMsg(TBDKey::InstallName));
942 // Handle optional keys.
943 Array Flags = serializeFlags(File);
944 insertNonEmptyValues(Library, TBDKey::Flags, std::move(Flags));
946 Array CurrentV = serializeScalar<PackedVersion, std::string>(
947 TBDKey::Version, File->getCurrentVersion(), PackedVersion(1, 0, 0));
948 insertNonEmptyValues(Library, TBDKey::CurrentVersion, std::move(CurrentV));
950 Array CompatV = serializeScalar<PackedVersion, std::string>(
951 TBDKey::Version, File->getCompatibilityVersion(), PackedVersion(1, 0, 0));
952 insertNonEmptyValues(Library, TBDKey::CompatibilityVersion,
953 std::move(CompatV));
955 Array SwiftABI = serializeScalar<uint8_t, int64_t>(
956 TBDKey::ABI, File->getSwiftABIVersion(), 0u);
957 insertNonEmptyValues(Library, TBDKey::SwiftABI, std::move(SwiftABI));
959 Array RPaths = serializeField(TBDKey::Paths, File->rpaths(), ActiveTargets);
960 insertNonEmptyValues(Library, TBDKey::RPath, std::move(RPaths));
962 Array Umbrellas = serializeField(TBDKey::Umbrella, File->umbrellas(),
963 ActiveTargets, /*IsArray=*/false);
964 insertNonEmptyValues(Library, TBDKey::ParentUmbrella, std::move(Umbrellas));
966 Array Clients =
967 serializeField(TBDKey::Clients, File->allowableClients(), ActiveTargets);
968 insertNonEmptyValues(Library, TBDKey::AllowableClients, std::move(Clients));
970 Array ReexportLibs =
971 serializeField(TBDKey::Names, File->reexportedLibraries(), ActiveTargets);
972 insertNonEmptyValues(Library, TBDKey::ReexportLibs, std::move(ReexportLibs));
974 // Handle symbols.
975 Array Exports = serializeSymbols(File->exports(), ActiveTargets);
976 insertNonEmptyValues(Library, TBDKey::Exports, std::move(Exports));
978 Array Reexports = serializeSymbols(File->reexports(), ActiveTargets);
979 insertNonEmptyValues(Library, TBDKey::Reexports, std::move(Reexports));
981 if (!File->isTwoLevelNamespace()) {
982 Array Undefineds = serializeSymbols(File->undefineds(), ActiveTargets);
983 insertNonEmptyValues(Library, TBDKey::Undefineds, std::move(Undefineds));
986 return std::move(Library);
989 Expected<Object> getJSON(const InterfaceFile *File, const FileType FileKind) {
990 assert(FileKind == FileType::TBD_V5 && "unexpected json file format version");
991 Object Root;
993 auto MainLibOrErr = serializeIF(File);
994 if (!MainLibOrErr)
995 return MainLibOrErr;
996 Root[Keys[TBDKey::MainLibrary]] = std::move(*MainLibOrErr);
997 Array Documents;
998 for (const auto &Doc : File->documents()) {
999 auto LibOrErr = serializeIF(Doc.get());
1000 if (!LibOrErr)
1001 return LibOrErr;
1002 Documents.emplace_back(std::move(*LibOrErr));
1005 Root[Keys[TBDKey::TBDVersion]] = 5;
1006 insertNonEmptyValues(Root, TBDKey::Documents, std::move(Documents));
1007 return std::move(Root);
1010 } // namespace
1012 Error MachO::serializeInterfaceFileToJSON(raw_ostream &OS,
1013 const InterfaceFile &File,
1014 const FileType FileKind,
1015 bool Compact) {
1016 auto TextFile = getJSON(&File, FileKind);
1017 if (!TextFile)
1018 return TextFile.takeError();
1019 if (Compact)
1020 OS << formatv("{0}", Value(std::move(*TextFile))) << "\n";
1021 else
1022 OS << formatv("{0:2}", Value(std::move(*TextFile))) << "\n";
1023 return Error::success();