1 //===- TextStubV5.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Implements Text Stub JSON mappings.
11 //===----------------------------------------------------------------------===//
12 #include "TextStubCommon.h"
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/Support/JSON.h"
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" # Required: minimum OS deployment version
33 "target": "arm64-macos",
34 "min_deployment": "10.14"
37 "target": "arm64-maccatalyst",
38 "min_deployment": "12.1"
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"]
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" ],
59 "data": { ... }, # List of Data segment symbols
61 "reexported_symbols": [{ ... }], # List of reexported symbols section
62 "undefined_symbols": [{ ... }] # List of undefined symbols section
64 "libraries": [ # Optional: Array of inlined libraries
72 using namespace llvm::json
;
73 using namespace llvm::MachO
;
81 using AttrToTargets
= std::map
<std::string
, TargetList
>;
82 using TargetsToSymbols
=
83 SmallVector
<std::pair
<TargetList
, std::vector
<JSONSymbol
>>>;
85 enum TBDKey
: size_t {
123 std::array
<StringRef
, 64> Keys
= {
135 "compatibility_versions",
143 "reexported_libraries",
147 "reexported_symbols",
161 static llvm::SmallString
<128> getParseErrorMsg(TBDKey Key
) {
162 return {"invalid ", Keys
[Key
], " section"};
165 static llvm::SmallString
<128> getSerializeErrorMsg(TBDKey Key
) {
166 return {"missing ", Keys
[Key
], " information"};
169 class JSONStubError
: public llvm::ErrorInfo
<llvm::json::ParseError
> {
171 JSONStubError(Twine ErrMsg
) : Message(ErrMsg
.str()) {}
173 void log(llvm::raw_ostream
&OS
) const override
{ OS
<< Message
<< "\n"; }
174 std::error_code
convertToErrorCode() const override
{
175 return llvm::inconvertibleErrorCode();
182 template <typename JsonT
, typename StubT
= JsonT
>
183 Expected
<StubT
> getRequiredValue(
184 TBDKey Key
, const Object
*Obj
,
185 std::function
<std::optional
<JsonT
>(const Object
*, StringRef
)> GetValue
,
186 std::function
<std::optional
<StubT
>(JsonT
)> Validate
= nullptr) {
187 std::optional
<JsonT
> Val
= GetValue(Obj
, Keys
[Key
]);
189 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
191 if (Validate
== nullptr)
192 return static_cast<StubT
>(*Val
);
194 std::optional
<StubT
> Result
= Validate(*Val
);
195 if (!Result
.has_value())
196 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
197 return Result
.value();
200 template <typename JsonT
, typename StubT
= JsonT
>
201 Expected
<StubT
> getRequiredValue(
202 TBDKey Key
, const Object
*Obj
,
203 std::function
<std::optional
<JsonT
>(const Object
*, StringRef
)> GetValue
,
204 StubT DefaultValue
, std::function
<std::optional
<StubT
>(JsonT
)> Validate
) {
205 std::optional
<JsonT
> Val
= GetValue(Obj
, Keys
[Key
]);
209 std::optional
<StubT
> Result
;
210 Result
= Validate(*Val
);
211 if (!Result
.has_value())
212 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
213 return Result
.value();
216 Error
collectFromArray(TBDKey Key
, const Object
*Obj
,
217 std::function
<void(StringRef
)> Append
,
218 bool IsRequired
= false) {
219 const auto *Values
= Obj
->getArray(Keys
[Key
]);
222 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
223 return Error::success();
226 for (const Value
&Val
: *Values
) {
227 auto ValStr
= Val
.getAsString();
228 if (!ValStr
.has_value())
229 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
230 Append(ValStr
.value());
233 return Error::success();
236 namespace StubParser
{
238 Expected
<FileType
> getVersion(const Object
*File
) {
239 auto VersionOrErr
= getRequiredValue
<int64_t, FileType
>(
240 TBDKey::TBDVersion
, File
, &Object::getInteger
,
241 [](int64_t Val
) -> std::optional
<FileType
> {
242 unsigned Result
= Val
;
245 return FileType::TBD_V5
;
249 return VersionOrErr
.takeError();
250 return *VersionOrErr
;
253 Expected
<TargetList
> getTargets(const Object
*Section
) {
254 const auto *Targets
= Section
->getArray(Keys
[TBDKey::Targets
]);
256 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Targets
));
258 TargetList IFTargets
;
259 for (const Value
&JSONTarget
: *Targets
) {
260 auto TargetStr
= JSONTarget
.getAsString();
261 if (!TargetStr
.has_value())
262 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Target
));
263 auto TargetOrErr
= Target::create(TargetStr
.value());
265 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Target
));
266 IFTargets
.push_back(*TargetOrErr
);
268 return std::move(IFTargets
);
271 Expected
<TargetList
> getTargetsSection(const Object
*Section
) {
272 const Array
*Targets
= Section
->getArray(Keys
[TBDKey::TargetInfo
]);
274 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Targets
));
276 TargetList IFTargets
;
277 for (const Value
&JSONTarget
: *Targets
) {
278 const auto *Obj
= JSONTarget
.getAsObject();
280 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Target
));
282 getRequiredValue
<StringRef
>(TBDKey::Target
, Obj
, &Object::getString
);
284 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Target
));
285 auto VersionStr
= getRequiredValue
<StringRef
>(TBDKey::Deployment
, Obj
,
288 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Deployment
));
289 VersionTuple Version
;
290 if (Version
.tryParse(*VersionStr
))
291 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Deployment
));
292 auto TargetOrErr
= Target::create(*TargetStr
);
294 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Target
));
295 TargetOrErr
->MinDeployment
= Version
;
296 // Convert to LLVM::Triple to accurately compute minOS + platform + arch
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
);
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
);
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
);
331 Err
= collectFromArray(
332 TBDKey::ObjCIvar
, Segment
, [&Result
, &SectionFlag
](StringRef Name
) {
333 JSONSymbol Sym
= {SymbolKind::ObjectiveCInstanceVariable
, Name
.str(),
335 Result
.back().second
.emplace_back(Sym
);
340 SymbolFlags WeakFlag
=
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
);
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
);
362 return Error::success();
365 Expected
<StringRef
> getNameSection(const Object
*File
) {
366 const Array
*Section
= File
->getArray(Keys
[TBDKey::InstallName
]);
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();
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
]);
384 return TargetsToSymbols();
386 SymbolFlags SectionFlag
;
388 case TBDKey::Reexports
:
389 SectionFlag
= SymbolFlags::Rexported
;
391 case TBDKey::Undefineds
:
392 SectionFlag
= SymbolFlags::Undefined
;
395 SectionFlag
= SymbolFlags::None
;
399 TargetsToSymbols Result
;
400 TargetList MappedTargets
;
401 for (auto Val
: *Section
) {
402 auto *Obj
= Val
.getAsObject();
406 auto TargetsOrErr
= getTargets(Obj
);
408 MappedTargets
= Targets
;
409 consumeError(TargetsOrErr
.takeError());
411 MappedTargets
= *TargetsOrErr
;
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
));
423 auto Err
= collectSymbolsFromSegment(DataSection
, Result
,
424 SectionFlag
| SymbolFlags::Data
);
426 return std::move(Err
);
429 auto Err
= collectSymbolsFromSegment(TextSection
, Result
,
430 SectionFlag
| SymbolFlags::Text
);
432 return std::move(Err
);
436 return std::move(Result
);
439 Expected
<AttrToTargets
> getLibSection(const Object
*File
, TBDKey Key
,
441 const TargetList
&Targets
) {
442 auto *Section
= File
->getArray(Keys
[Key
]);
444 return AttrToTargets();
446 AttrToTargets Result
;
447 TargetList MappedTargets
;
448 for (auto Val
: *Section
) {
449 auto *Obj
= Val
.getAsObject();
453 auto TargetsOrErr
= getTargets(Obj
);
455 MappedTargets
= Targets
;
456 consumeError(TargetsOrErr
.takeError());
458 MappedTargets
= *TargetsOrErr
;
461 collectFromArray(SubKey
, Obj
, [&Result
, &MappedTargets
](StringRef Key
) {
462 Result
[Key
.str()] = MappedTargets
;
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
]);
475 return AttrToTargets();
477 AttrToTargets Result
;
478 TargetList MappedTargets
;
479 for (auto Val
: *Umbrella
) {
480 auto *Obj
= Val
.getAsObject();
482 return make_error
<JSONStubError
>(
483 getParseErrorMsg(TBDKey::ParentUmbrella
));
485 // Get Targets section.
486 auto TargetsOrErr
= getTargets(Obj
);
488 MappedTargets
= Targets
;
489 consumeError(TargetsOrErr
.takeError());
491 MappedTargets
= *TargetsOrErr
;
495 getRequiredValue
<StringRef
>(TBDKey::Umbrella
, Obj
, &Object::getString
);
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
]);
508 for (const auto &Val
: *Versions
) {
509 const auto *Obj
= Val
.getAsObject();
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
);
521 Expected
<PackedVersion
> getPackedVersion(const Object
*File
, TBDKey Key
) {
522 const Array
*Versions
= File
->getArray(Keys
[Key
]);
524 return PackedVersion(1, 0, 0);
526 for (const auto &Val
: *Versions
) {
527 const auto *Obj
= Val
.getAsObject();
529 return make_error
<JSONStubError
>(getParseErrorMsg(Key
));
531 auto ValidatePV
= [](StringRef Version
) -> std::optional
<PackedVersion
> {
533 auto [success
, truncated
] = PV
.parse64(Version
);
534 if (!success
|| truncated
)
538 // TODO: Take first for now.
539 return getRequiredValue
<StringRef
, PackedVersion
>(
540 TBDKey::Version
, Obj
, &Object::getString
, PackedVersion(1, 0, 0),
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
]);
553 for (auto &Val
: *Section
) {
554 // TODO: Just take first for now.
555 const auto *Obj
= Val
.getAsObject();
557 return make_error
<JSONStubError
>(getParseErrorMsg(TBDKey::Flags
));
560 collectFromArray(TBDKey::Attributes
, Obj
, [&Flags
](StringRef Flag
) {
562 StringSwitch
<TBDFlags
>(Flag
)
563 .Case("flat_namespace", TBDFlags::FlatNamespace
)
564 .Case("not_app_extension_safe",
565 TBDFlags::NotApplicationExtensionSafe
)
566 .Default(TBDFlags::None
);
571 return std::move(FlagsOrErr
);
579 using IFPtr
= std::unique_ptr
<InterfaceFile
>;
580 Expected
<IFPtr
> parseToInterfaceFile(const Object
*File
) {
581 auto TargetsOrErr
= getTargetsSection(File
);
583 return TargetsOrErr
.takeError();
584 TargetList Targets
= *TargetsOrErr
;
586 auto NameOrErr
= getNameSection(File
);
588 return NameOrErr
.takeError();
589 StringRef Name
= *NameOrErr
;
591 auto CurrVersionOrErr
= getPackedVersion(File
, TBDKey::CurrentVersion
);
592 if (!CurrVersionOrErr
)
593 return CurrVersionOrErr
.takeError();
594 PackedVersion CurrVersion
= *CurrVersionOrErr
;
596 auto CompVersionOrErr
= getPackedVersion(File
, TBDKey::CompatibilityVersion
);
597 if (!CompVersionOrErr
)
598 return CompVersionOrErr
.takeError();
599 PackedVersion CompVersion
= *CompVersionOrErr
;
601 auto SwiftABIOrErr
= getSwiftVersion(File
);
603 return SwiftABIOrErr
.takeError();
604 uint8_t SwiftABI
= *SwiftABIOrErr
;
606 auto FlagsOrErr
= getFlags(File
);
608 return FlagsOrErr
.takeError();
609 TBDFlags Flags
= *FlagsOrErr
;
611 auto UmbrellasOrErr
= getUmbrellaSection(File
, Targets
);
613 return UmbrellasOrErr
.takeError();
614 AttrToTargets Umbrellas
= *UmbrellasOrErr
;
617 getLibSection(File
, TBDKey::AllowableClients
, TBDKey::Clients
, Targets
);
619 return ClientsOrErr
.takeError();
620 AttrToTargets Clients
= *ClientsOrErr
;
623 getLibSection(File
, TBDKey::ReexportLibs
, TBDKey::Names
, Targets
);
625 return RLOrErr
.takeError();
626 AttrToTargets ReexportLibs
= std::move(*RLOrErr
);
628 auto RPathsOrErr
= getLibSection(File
, TBDKey::RPath
, TBDKey::Paths
, Targets
);
630 return RPathsOrErr
.takeError();
631 AttrToTargets RPaths
= std::move(*RPathsOrErr
);
633 auto ExportsOrErr
= getSymbolSection(File
, TBDKey::Exports
, Targets
);
635 return ExportsOrErr
.takeError();
636 TargetsToSymbols Exports
= std::move(*ExportsOrErr
);
638 auto ReexportsOrErr
= getSymbolSection(File
, TBDKey::Reexports
, Targets
);
640 return ReexportsOrErr
.takeError();
641 TargetsToSymbols Reexports
= std::move(*ReexportsOrErr
);
643 auto UndefinedsOrErr
= getSymbolSection(File
, TBDKey::Undefineds
, Targets
);
644 if (!UndefinedsOrErr
)
645 return UndefinedsOrErr
.takeError();
646 TargetsToSymbols Undefineds
= std::move(*UndefinedsOrErr
);
648 IFPtr
F(new InterfaceFile
);
649 F
->setInstallName(Name
);
650 F
->setCurrentVersion(CurrVersion
);
651 F
->setCompatibilityVersion(CompVersion
);
652 F
->setSwiftABIVersion(SwiftABI
);
653 F
->setTwoLevelNamespace(!(Flags
& TBDFlags::FlatNamespace
));
654 F
->setApplicationExtensionSafe(
655 !(Flags
& TBDFlags::NotApplicationExtensionSafe
));
656 for (auto &T
: Targets
)
658 for (auto &[Lib
, Targets
] : Clients
)
659 for (auto Target
: Targets
)
660 F
->addAllowableClient(Lib
, Target
);
661 for (auto &[Lib
, Targets
] : ReexportLibs
)
662 for (auto Target
: Targets
)
663 F
->addReexportedLibrary(Lib
, Target
);
664 for (auto &[Lib
, Targets
] : Umbrellas
)
665 for (auto Target
: Targets
)
666 F
->addParentUmbrella(Target
, Lib
);
667 for (auto &[Path
, Targets
] : RPaths
)
668 for (auto Target
: Targets
)
669 F
->addRPath(Target
, Path
);
670 for (auto &[Targets
, Symbols
] : Exports
)
671 for (auto &Sym
: Symbols
)
672 F
->addSymbol(Sym
.Kind
, Sym
.Name
, Targets
, Sym
.Flags
);
673 for (auto &[Targets
, Symbols
] : Reexports
)
674 for (auto &Sym
: Symbols
)
675 F
->addSymbol(Sym
.Kind
, Sym
.Name
, Targets
, Sym
.Flags
);
676 for (auto &[Targets
, Symbols
] : Undefineds
)
677 for (auto &Sym
: Symbols
)
678 F
->addSymbol(Sym
.Kind
, Sym
.Name
, Targets
, Sym
.Flags
);
683 Expected
<std::vector
<IFPtr
>> getInlinedLibs(const Object
*File
) {
684 std::vector
<IFPtr
> IFs
;
685 const Array
*Files
= File
->getArray(Keys
[TBDKey::Documents
]);
687 return std::move(IFs
);
689 for (auto Lib
: *Files
) {
690 auto IFOrErr
= parseToInterfaceFile(Lib
.getAsObject());
692 return IFOrErr
.takeError();
693 auto IF
= std::move(*IFOrErr
);
694 IFs
.emplace_back(std::move(IF
));
696 return std::move(IFs
);
699 } // namespace StubParser
701 Expected
<std::unique_ptr
<InterfaceFile
>>
702 MachO::getInterfaceFileFromJSON(StringRef JSON
) {
703 auto ValOrErr
= parse(JSON
);
705 return ValOrErr
.takeError();
707 auto *Root
= ValOrErr
->getAsObject();
708 auto VersionOrErr
= StubParser::getVersion(Root
);
710 return VersionOrErr
.takeError();
711 FileType Version
= *VersionOrErr
;
713 Object
*MainLib
= Root
->getObject(Keys
[TBDKey::MainLibrary
]);
714 auto IFOrErr
= StubParser::parseToInterfaceFile(MainLib
);
716 return IFOrErr
.takeError();
717 (*IFOrErr
)->setFileType(Version
);
718 std::unique_ptr
<InterfaceFile
> IF(std::move(*IFOrErr
));
720 auto IFsOrErr
= StubParser::getInlinedLibs(Root
);
722 return IFsOrErr
.takeError();
723 for (auto &File
: *IFsOrErr
) {
724 File
->setFileType(Version
);
725 IF
->addDocument(std::shared_ptr
<InterfaceFile
>(std::move(File
)));
727 return std::move(IF
);
732 template <typename ContainerT
= Array
>
733 bool insertNonEmptyValues(Object
&Obj
, TBDKey Key
, ContainerT
&&Contents
) {
734 if (Contents
.empty())
736 Obj
[Keys
[Key
]] = std::move(Contents
);
740 std::string
getFormattedStr(const MachO::Target
&Targ
) {
741 std::string PlatformStr
= Targ
.Platform
== PLATFORM_MACCATALYST
743 : getOSAndEnvironmentName(Targ
.Platform
);
744 return (getArchitectureName(Targ
.Arch
) + "-" + PlatformStr
).str();
747 template <typename AggregateT
>
748 std::vector
<std::string
> serializeTargets(const AggregateT Targets
,
749 const TargetList
&ActiveTargets
) {
750 std::vector
<std::string
> TargetsStr
;
751 if (Targets
.size() == ActiveTargets
.size())
754 llvm::for_each(Targets
, [&TargetsStr
](const MachO::Target
&Target
) {
755 TargetsStr
.emplace_back(getFormattedStr(Target
));
760 Array
serializeTargetInfo(const TargetList
&ActiveTargets
) {
762 for (const auto Targ
: ActiveTargets
) {
764 TargetInfo
[Keys
[TBDKey::Deployment
]] = Targ
.MinDeployment
.getAsString();
765 TargetInfo
[Keys
[TBDKey::Target
]] = getFormattedStr(Targ
);
766 Targets
.emplace_back(std::move(TargetInfo
));
771 template <typename ValueT
, typename EntryT
= ValueT
>
772 Array
serializeScalar(TBDKey Key
, ValueT Value
, ValueT Default
= ValueT()) {
773 if (Value
== Default
)
776 Object
ScalarObj({Object::KV({Keys
[Key
], EntryT(Value
)})});
778 Container
.emplace_back(std::move(ScalarObj
));
782 using TargetsToValuesMap
=
783 std::map
<std::vector
<std::string
>, std::vector
<std::string
>>;
785 template <typename AggregateT
= TargetsToValuesMap
>
786 Array
serializeAttrToTargets(AggregateT
&Entries
, TBDKey Key
) {
788 for (const auto &[Targets
, Values
] : Entries
) {
790 insertNonEmptyValues(Obj
, TBDKey::Targets
, std::move(Targets
));
791 Obj
[Keys
[Key
]] = Values
;
792 Container
.emplace_back(std::move(Obj
));
797 template <typename ValueT
= std::string
,
798 typename AggregateT
= std::vector
<std::pair
<MachO::Target
, ValueT
>>>
799 Array
serializeField(TBDKey Key
, const AggregateT
&Values
,
800 const TargetList
&ActiveTargets
, bool IsArray
= true) {
801 std::map
<ValueT
, std::set
<MachO::Target
>> Entries
;
802 for (const auto &[Target
, Val
] : Values
)
803 Entries
[Val
].insert(Target
);
806 std::map
<std::vector
<std::string
>, std::string
> FinalEntries
;
807 for (const auto &[Val
, Targets
] : Entries
)
808 FinalEntries
[serializeTargets(Targets
, ActiveTargets
)] = Val
;
809 return serializeAttrToTargets(FinalEntries
, Key
);
812 TargetsToValuesMap FinalEntries
;
813 for (const auto &[Val
, Targets
] : Entries
)
814 FinalEntries
[serializeTargets(Targets
, ActiveTargets
)].emplace_back(Val
);
815 return serializeAttrToTargets(FinalEntries
, Key
);
818 Array
serializeField(TBDKey Key
, const std::vector
<InterfaceFileRef
> &Values
,
819 const TargetList
&ActiveTargets
) {
820 TargetsToValuesMap FinalEntries
;
821 for (const auto &Ref
: Values
) {
822 TargetList Targets
{Ref
.targets().begin(), Ref
.targets().end()};
823 FinalEntries
[serializeTargets(Targets
, ActiveTargets
)].emplace_back(
824 Ref
.getInstallName());
826 return serializeAttrToTargets(FinalEntries
, Key
);
829 struct SymbolFields
{
831 std::vector
<StringRef
> Weaks
;
832 std::vector
<StringRef
> Globals
;
833 std::vector
<StringRef
> TLV
;
834 std::vector
<StringRef
> ObjCClasses
;
835 std::vector
<StringRef
> IVars
;
836 std::vector
<StringRef
> EHTypes
;
839 return Weaks
.empty() && Globals
.empty() && TLV
.empty() &&
840 ObjCClasses
.empty() && IVars
.empty() && EHTypes
.empty();
847 Array
serializeSymbols(InterfaceFile::const_filtered_symbol_range Symbols
,
848 const TargetList
&ActiveTargets
) {
849 auto AssignForSymbolType
= [](SymbolFields::SymbolTypes
&Assignment
,
851 switch (Sym
->getKind()) {
852 case SymbolKind::ObjectiveCClass
:
853 Assignment
.ObjCClasses
.emplace_back(Sym
->getName());
855 case SymbolKind::ObjectiveCClassEHType
:
856 Assignment
.EHTypes
.emplace_back(Sym
->getName());
858 case SymbolKind::ObjectiveCInstanceVariable
:
859 Assignment
.IVars
.emplace_back(Sym
->getName());
861 case SymbolKind::GlobalSymbol
: {
862 if (Sym
->isWeakReferenced() || Sym
->isWeakDefined())
863 Assignment
.Weaks
.emplace_back(Sym
->getName());
864 else if (Sym
->isThreadLocalValue())
865 Assignment
.TLV
.emplace_back(Sym
->getName());
867 Assignment
.Globals
.emplace_back(Sym
->getName());
873 std::map
<std::vector
<std::string
>, SymbolFields
> Entries
;
874 for (const auto *Sym
: Symbols
) {
875 std::set
<MachO::Target
> Targets
{Sym
->targets().begin(),
876 Sym
->targets().end()};
877 auto JSONTargets
= serializeTargets(Targets
, ActiveTargets
);
879 AssignForSymbolType(Entries
[std::move(JSONTargets
)].Data
, Sym
);
880 else if (Sym
->isText())
881 AssignForSymbolType(Entries
[std::move(JSONTargets
)].Text
, Sym
);
883 llvm_unreachable("unexpected symbol type");
886 auto InsertSymbolsToJSON
= [](Object
&SymSection
, TBDKey SegmentKey
,
887 SymbolFields::SymbolTypes
&SymField
) {
888 if (SymField
.empty())
891 insertNonEmptyValues(Segment
, TBDKey::Globals
, std::move(SymField
.Globals
));
892 insertNonEmptyValues(Segment
, TBDKey::ThreadLocal
, std::move(SymField
.TLV
));
893 insertNonEmptyValues(Segment
, TBDKey::Weak
, std::move(SymField
.Weaks
));
894 insertNonEmptyValues(Segment
, TBDKey::ObjCClass
,
895 std::move(SymField
.ObjCClasses
));
896 insertNonEmptyValues(Segment
, TBDKey::ObjCEHType
,
897 std::move(SymField
.EHTypes
));
898 insertNonEmptyValues(Segment
, TBDKey::ObjCIvar
, std::move(SymField
.IVars
));
899 insertNonEmptyValues(SymSection
, SegmentKey
, std::move(Segment
));
903 for (auto &[Targets
, Fields
] : Entries
) {
905 insertNonEmptyValues(AllSyms
, TBDKey::Targets
, std::move(Targets
));
906 InsertSymbolsToJSON(AllSyms
, TBDKey::Data
, Fields
.Data
);
907 InsertSymbolsToJSON(AllSyms
, TBDKey::Text
, Fields
.Text
);
908 SymbolSection
.emplace_back(std::move(AllSyms
));
911 return SymbolSection
;
914 Array
serializeFlags(const InterfaceFile
*File
) {
915 // TODO: Give all Targets the same flags for now.
917 if (!File
->isTwoLevelNamespace())
918 Flags
.emplace_back("flat_namespace");
919 if (!File
->isApplicationExtensionSafe())
920 Flags
.emplace_back("not_app_extension_safe");
921 return serializeScalar(TBDKey::Attributes
, std::move(Flags
));
924 Expected
<Object
> serializeIF(const InterfaceFile
*File
) {
927 // Handle required keys.
928 TargetList ActiveTargets
{File
->targets().begin(), File
->targets().end()};
929 if (!insertNonEmptyValues(Library
, TBDKey::TargetInfo
,
930 serializeTargetInfo(ActiveTargets
)))
931 return make_error
<JSONStubError
>(getSerializeErrorMsg(TBDKey::TargetInfo
));
933 Array Name
= serializeScalar
<StringRef
>(TBDKey::Name
, File
->getInstallName());
934 if (!insertNonEmptyValues(Library
, TBDKey::InstallName
, std::move(Name
)))
935 return make_error
<JSONStubError
>(getSerializeErrorMsg(TBDKey::InstallName
));
937 // Handle optional keys.
938 Array Flags
= serializeFlags(File
);
939 insertNonEmptyValues(Library
, TBDKey::Flags
, std::move(Flags
));
941 Array CurrentV
= serializeScalar
<PackedVersion
, std::string
>(
942 TBDKey::Version
, File
->getCurrentVersion(), PackedVersion(1, 0, 0));
943 insertNonEmptyValues(Library
, TBDKey::CurrentVersion
, std::move(CurrentV
));
945 Array CompatV
= serializeScalar
<PackedVersion
, std::string
>(
946 TBDKey::Version
, File
->getCompatibilityVersion(), PackedVersion(1, 0, 0));
947 insertNonEmptyValues(Library
, TBDKey::CompatibilityVersion
,
950 Array SwiftABI
= serializeScalar
<uint8_t, int64_t>(
951 TBDKey::ABI
, File
->getSwiftABIVersion(), 0u);
952 insertNonEmptyValues(Library
, TBDKey::SwiftABI
, std::move(SwiftABI
));
954 Array RPaths
= serializeField(TBDKey::Paths
, File
->rpaths(), ActiveTargets
);
955 insertNonEmptyValues(Library
, TBDKey::RPath
, std::move(RPaths
));
957 Array Umbrellas
= serializeField(TBDKey::Umbrella
, File
->umbrellas(),
958 ActiveTargets
, /*IsArray=*/false);
959 insertNonEmptyValues(Library
, TBDKey::ParentUmbrella
, std::move(Umbrellas
));
962 serializeField(TBDKey::Clients
, File
->allowableClients(), ActiveTargets
);
963 insertNonEmptyValues(Library
, TBDKey::AllowableClients
, std::move(Clients
));
966 serializeField(TBDKey::Names
, File
->reexportedLibraries(), ActiveTargets
);
967 insertNonEmptyValues(Library
, TBDKey::ReexportLibs
, std::move(ReexportLibs
));
970 Array Exports
= serializeSymbols(File
->exports(), ActiveTargets
);
971 insertNonEmptyValues(Library
, TBDKey::Exports
, std::move(Exports
));
973 Array Reexports
= serializeSymbols(File
->reexports(), ActiveTargets
);
974 insertNonEmptyValues(Library
, TBDKey::Reexports
, std::move(Reexports
));
976 if (!File
->isTwoLevelNamespace()) {
977 Array Undefineds
= serializeSymbols(File
->undefineds(), ActiveTargets
);
978 insertNonEmptyValues(Library
, TBDKey::Undefineds
, std::move(Undefineds
));
981 return std::move(Library
);
984 Expected
<Object
> getJSON(const InterfaceFile
*File
) {
985 assert(File
->getFileType() == FileType::TBD_V5
&&
986 "unexpected json file format version");
989 auto MainLibOrErr
= serializeIF(File
);
992 Root
[Keys
[TBDKey::MainLibrary
]] = std::move(*MainLibOrErr
);
994 for (const auto &Doc
: File
->documents()) {
995 auto LibOrErr
= serializeIF(Doc
.get());
998 Documents
.emplace_back(std::move(*LibOrErr
));
1001 Root
[Keys
[TBDKey::TBDVersion
]] = 5;
1002 insertNonEmptyValues(Root
, TBDKey::Documents
, std::move(Documents
));
1003 return std::move(Root
);
1008 Error
MachO::serializeInterfaceFileToJSON(raw_ostream
&OS
,
1009 const InterfaceFile
&File
,
1011 auto TextFile
= getJSON(&File
);
1013 return TextFile
.takeError();
1015 OS
<< formatv("{0}", Value(std::move(*TextFile
))) << "\n";
1017 OS
<< formatv("{0:2}", Value(std::move(*TextFile
))) << "\n";
1018 return Error::success();