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