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