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 | enum TBDKey : size_t { |
87 | TBDVersion = 0U, |
88 | MainLibrary, |
89 | Documents, |
90 | TargetInfo, |
91 | Targets, |
92 | Target, |
93 | Deployment, |
94 | Flags, |
95 | Attributes, |
96 | InstallName, |
97 | CurrentVersion, |
98 | CompatibilityVersion, |
99 | Version, |
100 | SwiftABI, |
101 | ABI, |
102 | ParentUmbrella, |
103 | Umbrella, |
104 | AllowableClients, |
105 | Clients, |
106 | ReexportLibs, |
107 | Names, |
108 | Name, |
109 | Exports, |
110 | Reexports, |
111 | Undefineds, |
112 | Data, |
113 | Text, |
114 | Weak, |
115 | ThreadLocal, |
116 | Globals, |
117 | ObjCClass, |
118 | ObjCEHType, |
119 | ObjCIvar, |
120 | RPath, |
121 | Paths, |
122 | }; |
123 | |
124 | std::array<StringRef, 64> Keys = { |
125 | "tapi_tbd_version" , |
126 | "main_library" , |
127 | "libraries" , |
128 | "target_info" , |
129 | "targets" , |
130 | "target" , |
131 | "min_deployment" , |
132 | "flags" , |
133 | "attributes" , |
134 | "install_names" , |
135 | "current_versions" , |
136 | "compatibility_versions" , |
137 | "version" , |
138 | "swift_abi" , |
139 | "abi" , |
140 | "parent_umbrellas" , |
141 | "umbrella" , |
142 | "allowable_clients" , |
143 | "clients" , |
144 | "reexported_libraries" , |
145 | "names" , |
146 | "name" , |
147 | "exported_symbols" , |
148 | "reexported_symbols" , |
149 | "undefined_symbols" , |
150 | "data" , |
151 | "text" , |
152 | "weak" , |
153 | "thread_local" , |
154 | "global" , |
155 | "objc_class" , |
156 | "objc_eh_type" , |
157 | "objc_ivar" , |
158 | "rpaths" , |
159 | "paths" , |
160 | }; |
161 | |
162 | static llvm::SmallString<128> getParseErrorMsg(TBDKey Key) { |
163 | return {"invalid " , Keys[Key], " section" }; |
164 | } |
165 | |
166 | static llvm::SmallString<128> getSerializeErrorMsg(TBDKey Key) { |
167 | return {"missing " , Keys[Key], " information" }; |
168 | } |
169 | |
170 | class JSONStubError : public llvm::ErrorInfo<llvm::json::ParseError> { |
171 | public: |
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 | |
179 | private: |
180 | std::string Message; |
181 | }; |
182 | |
183 | template <typename JsonT, typename StubT = JsonT> |
184 | Expected<StubT> getRequiredValue( |
185 | TBDKey Key, const Object *Obj, |
186 | std::function<std::optional<JsonT>(const Object *, StringRef)> GetValue, |
187 | std::function<std::optional<StubT>(JsonT)> Validate = nullptr) { |
188 | std::optional<JsonT> Val = GetValue(Obj, Keys[Key]); |
189 | if (!Val) |
190 | return make_error<JSONStubError>(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 | |
201 | template <typename JsonT, typename StubT = JsonT> |
202 | Expected<StubT> getRequiredValue( |
203 | TBDKey Key, const Object *Obj, |
204 | std::function<std::optional<JsonT>(const Object *, StringRef)> 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 | |
218 | Error 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 | |
238 | namespace StubParser { |
239 | |
240 | Expected<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 | |
255 | Expected<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 | |
273 | Expected<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 | |
305 | Error 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 | |
366 | Expected<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 | |
380 | Expected<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 | |
440 | Expected<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 | |
472 | Expected<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 | |
504 | Expected<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 | |
522 | Expected<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 | |
548 | Expected<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 | |
583 | using IFPtr = std::unique_ptr<InterfaceFile>; |
584 | Expected<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 | |
689 | Expected<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 | |
708 | Expected<std::unique_ptr<InterfaceFile>> |
709 | MachO::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 | |
737 | namespace { |
738 | |
739 | template <typename ContainerT = Array> |
740 | bool 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 | |
747 | std::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 | |
754 | template <typename AggregateT> |
755 | std::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 | |
767 | Array 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 | |
779 | template <typename ValueT, typename EntryT = ValueT> |
780 | Array 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 | |
790 | using TargetsToValuesMap = |
791 | std::map<std::vector<std::string>, std::vector<std::string>>; |
792 | |
793 | template <typename AggregateT = TargetsToValuesMap> |
794 | Array 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 | |
805 | template <typename ValueT = std::string, |
806 | typename AggregateT = std::vector<std::pair<MachO::Target, ValueT>>> |
807 | Array 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 | |
826 | Array 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 | |
837 | struct 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 | |
855 | Array 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 | |
922 | Array 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 | |
936 | Expected<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 | |
996 | Expected<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 | |
1019 | Error 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 | |