1 | //===-- APINotesWriter.cpp - API Notes Writer -------------------*- C++ -*-===// |
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 | #include "clang/APINotes/APINotesWriter.h" |
10 | #include "APINotesFormat.h" |
11 | #include "clang/APINotes/Types.h" |
12 | #include "clang/Basic/FileManager.h" |
13 | #include "llvm/ADT/DenseMap.h" |
14 | #include "llvm/ADT/StringMap.h" |
15 | #include "llvm/Bitstream/BitstreamWriter.h" |
16 | #include "llvm/Support/DJB.h" |
17 | #include "llvm/Support/OnDiskHashTable.h" |
18 | #include "llvm/Support/VersionTuple.h" |
19 | |
20 | namespace clang { |
21 | namespace api_notes { |
22 | class APINotesWriter::Implementation { |
23 | friend class APINotesWriter; |
24 | |
25 | template <typename T> |
26 | using VersionedSmallVector = |
27 | llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1>; |
28 | |
29 | std::string ModuleName; |
30 | const FileEntry *SourceFile; |
31 | |
32 | /// Scratch space for bitstream writing. |
33 | llvm::SmallVector<uint64_t, 64> Scratch; |
34 | |
35 | /// Mapping from strings to identifier IDs. |
36 | llvm::StringMap<IdentifierID> IdentifierIDs; |
37 | |
38 | /// Information about contexts (Objective-C classes or protocols or C++ |
39 | /// namespaces). |
40 | /// |
41 | /// Indexed by the parent context ID, context kind and the identifier ID of |
42 | /// this context and provides both the context ID and information describing |
43 | /// the context within that module. |
44 | llvm::DenseMap<ContextTableKey, |
45 | std::pair<unsigned, VersionedSmallVector<ObjCContextInfo>>> |
46 | ObjCContexts; |
47 | |
48 | /// Information about parent contexts for each context. |
49 | /// |
50 | /// Indexed by context ID, provides the parent context ID. |
51 | llvm::DenseMap<uint32_t, uint32_t> ParentContexts; |
52 | |
53 | /// Mapping from context IDs to the identifier ID holding the name. |
54 | llvm::DenseMap<unsigned, unsigned> ObjCContextNames; |
55 | |
56 | /// Information about Objective-C properties. |
57 | /// |
58 | /// Indexed by the context ID, property name, and whether this is an |
59 | /// instance property. |
60 | llvm::DenseMap< |
61 | std::tuple<unsigned, unsigned, char>, |
62 | llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, 1>> |
63 | ObjCProperties; |
64 | |
65 | /// Information about Objective-C methods. |
66 | /// |
67 | /// Indexed by the context ID, selector ID, and Boolean (stored as a char) |
68 | /// indicating whether this is a class or instance method. |
69 | llvm::DenseMap<std::tuple<unsigned, unsigned, char>, |
70 | llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>> |
71 | ObjCMethods; |
72 | |
73 | /// Mapping from selectors to selector ID. |
74 | llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs; |
75 | |
76 | /// Information about global variables. |
77 | /// |
78 | /// Indexed by the context ID, contextKind, identifier ID. |
79 | llvm::DenseMap< |
80 | ContextTableKey, |
81 | llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, 1>> |
82 | GlobalVariables; |
83 | |
84 | /// Information about global functions. |
85 | /// |
86 | /// Indexed by the context ID, contextKind, identifier ID. |
87 | llvm::DenseMap< |
88 | ContextTableKey, |
89 | llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>, 1>> |
90 | GlobalFunctions; |
91 | |
92 | /// Information about enumerators. |
93 | /// |
94 | /// Indexed by the identifier ID. |
95 | llvm::DenseMap< |
96 | unsigned, llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>, 1>> |
97 | EnumConstants; |
98 | |
99 | /// Information about tags. |
100 | /// |
101 | /// Indexed by the context ID, contextKind, identifier ID. |
102 | llvm::DenseMap<ContextTableKey, |
103 | llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>> |
104 | Tags; |
105 | |
106 | /// Information about typedefs. |
107 | /// |
108 | /// Indexed by the context ID, contextKind, identifier ID. |
109 | llvm::DenseMap<ContextTableKey, |
110 | llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>> |
111 | Typedefs; |
112 | |
113 | /// Retrieve the ID for the given identifier. |
114 | IdentifierID getIdentifier(StringRef Identifier) { |
115 | if (Identifier.empty()) |
116 | return 0; |
117 | |
118 | auto Known = IdentifierIDs.find(Key: Identifier); |
119 | if (Known != IdentifierIDs.end()) |
120 | return Known->second; |
121 | |
122 | // Add to the identifier table. |
123 | Known = IdentifierIDs.insert(KV: {Identifier, IdentifierIDs.size() + 1}).first; |
124 | return Known->second; |
125 | } |
126 | |
127 | /// Retrieve the ID for the given selector. |
128 | SelectorID getSelector(ObjCSelectorRef SelectorRef) { |
129 | // Translate the selector reference into a stored selector. |
130 | StoredObjCSelector Selector; |
131 | Selector.NumArgs = SelectorRef.NumArgs; |
132 | Selector.Identifiers.reserve(N: SelectorRef.Identifiers.size()); |
133 | for (auto piece : SelectorRef.Identifiers) |
134 | Selector.Identifiers.push_back(Elt: getIdentifier(Identifier: piece)); |
135 | |
136 | // Look for the stored selector. |
137 | auto Known = SelectorIDs.find(Val: Selector); |
138 | if (Known != SelectorIDs.end()) |
139 | return Known->second; |
140 | |
141 | // Add to the selector table. |
142 | Known = SelectorIDs.insert(KV: {Selector, SelectorIDs.size()}).first; |
143 | return Known->second; |
144 | } |
145 | |
146 | private: |
147 | void writeBlockInfoBlock(llvm::BitstreamWriter &Stream); |
148 | void writeControlBlock(llvm::BitstreamWriter &Stream); |
149 | void writeIdentifierBlock(llvm::BitstreamWriter &Stream); |
150 | void writeObjCContextBlock(llvm::BitstreamWriter &Stream); |
151 | void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream); |
152 | void writeObjCMethodBlock(llvm::BitstreamWriter &Stream); |
153 | void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream); |
154 | void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream); |
155 | void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream); |
156 | void writeEnumConstantBlock(llvm::BitstreamWriter &Stream); |
157 | void writeTagBlock(llvm::BitstreamWriter &Stream); |
158 | void writeTypedefBlock(llvm::BitstreamWriter &Stream); |
159 | |
160 | public: |
161 | Implementation(llvm::StringRef ModuleName, const FileEntry *SF) |
162 | : ModuleName(std::string(ModuleName)), SourceFile(SF) {} |
163 | |
164 | void writeToStream(llvm::raw_ostream &OS); |
165 | }; |
166 | |
167 | void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) { |
168 | llvm::SmallVector<char, 0> Buffer; |
169 | |
170 | { |
171 | llvm::BitstreamWriter Stream(Buffer); |
172 | |
173 | // Emit the signature. |
174 | for (unsigned char Byte : API_NOTES_SIGNATURE) |
175 | Stream.Emit(Val: Byte, NumBits: 8); |
176 | |
177 | // Emit the blocks. |
178 | writeBlockInfoBlock(Stream); |
179 | writeControlBlock(Stream); |
180 | writeIdentifierBlock(Stream); |
181 | writeObjCContextBlock(Stream); |
182 | writeObjCPropertyBlock(Stream); |
183 | writeObjCMethodBlock(Stream); |
184 | writeObjCSelectorBlock(Stream); |
185 | writeGlobalVariableBlock(Stream); |
186 | writeGlobalFunctionBlock(Stream); |
187 | writeEnumConstantBlock(Stream); |
188 | writeTagBlock(Stream); |
189 | writeTypedefBlock(Stream); |
190 | } |
191 | |
192 | OS.write(Ptr: Buffer.data(), Size: Buffer.size()); |
193 | OS.flush(); |
194 | } |
195 | |
196 | namespace { |
197 | /// Record the name of a block. |
198 | void emitBlockID(llvm::BitstreamWriter &Stream, unsigned ID, |
199 | llvm::StringRef Name) { |
200 | Stream.EmitRecord(Code: llvm::bitc::BLOCKINFO_CODE_SETBID, |
201 | Vals: llvm::ArrayRef<unsigned>{ID}); |
202 | |
203 | // Emit the block name if present. |
204 | if (Name.empty()) |
205 | return; |
206 | Stream.EmitRecord( |
207 | Code: llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, |
208 | Vals: llvm::ArrayRef<unsigned char>( |
209 | const_cast<unsigned char *>( |
210 | reinterpret_cast<const unsigned char *>(Name.data())), |
211 | Name.size())); |
212 | } |
213 | |
214 | /// Record the name of a record within a block. |
215 | void emitRecordID(llvm::BitstreamWriter &Stream, unsigned ID, |
216 | llvm::StringRef Name) { |
217 | assert(ID < 256 && "can't fit record ID in next to name" ); |
218 | |
219 | llvm::SmallVector<unsigned char, 64> Buffer; |
220 | Buffer.resize(N: Name.size() + 1); |
221 | Buffer[0] = ID; |
222 | memcpy(dest: Buffer.data() + 1, src: Name.data(), n: Name.size()); |
223 | |
224 | Stream.EmitRecord(Code: llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Vals: Buffer); |
225 | } |
226 | } // namespace |
227 | |
228 | void APINotesWriter::Implementation::writeBlockInfoBlock( |
229 | llvm::BitstreamWriter &Stream) { |
230 | llvm::BCBlockRAII Scope(Stream, llvm::bitc::BLOCKINFO_BLOCK_ID, 2); |
231 | |
232 | #define BLOCK(Block) emitBlockID(Stream, Block##_ID, #Block) |
233 | #define BLOCK_RECORD(NameSpace, Block) \ |
234 | emitRecordID(Stream, NameSpace::Block, #Block) |
235 | BLOCK(CONTROL_BLOCK); |
236 | BLOCK_RECORD(control_block, METADATA); |
237 | BLOCK_RECORD(control_block, MODULE_NAME); |
238 | |
239 | BLOCK(IDENTIFIER_BLOCK); |
240 | BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); |
241 | |
242 | BLOCK(OBJC_CONTEXT_BLOCK); |
243 | BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA); |
244 | |
245 | BLOCK(OBJC_PROPERTY_BLOCK); |
246 | BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); |
247 | |
248 | BLOCK(OBJC_METHOD_BLOCK); |
249 | BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA); |
250 | |
251 | BLOCK(OBJC_SELECTOR_BLOCK); |
252 | BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA); |
253 | |
254 | BLOCK(GLOBAL_VARIABLE_BLOCK); |
255 | BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA); |
256 | |
257 | BLOCK(GLOBAL_FUNCTION_BLOCK); |
258 | BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA); |
259 | #undef BLOCK_RECORD |
260 | #undef BLOCK |
261 | } |
262 | |
263 | void APINotesWriter::Implementation::writeControlBlock( |
264 | llvm::BitstreamWriter &Stream) { |
265 | llvm::BCBlockRAII Scope(Stream, CONTROL_BLOCK_ID, 3); |
266 | |
267 | control_block::MetadataLayout Metadata(Stream); |
268 | Metadata.emit(buffer&: Scratch, data: VERSION_MAJOR, data: VERSION_MINOR); |
269 | |
270 | control_block::ModuleNameLayout ModuleName(Stream); |
271 | ModuleName.emit(buffer&: Scratch, data&: this->ModuleName); |
272 | |
273 | if (SourceFile) { |
274 | control_block::SourceFileLayout SourceFile(Stream); |
275 | SourceFile.emit(buffer&: Scratch, data: this->SourceFile->getSize(), |
276 | data: this->SourceFile->getModificationTime()); |
277 | } |
278 | } |
279 | |
280 | namespace { |
281 | /// Used to serialize the on-disk identifier table. |
282 | class IdentifierTableInfo { |
283 | public: |
284 | using key_type = StringRef; |
285 | using key_type_ref = key_type; |
286 | using data_type = IdentifierID; |
287 | using data_type_ref = const data_type &; |
288 | using hash_value_type = uint32_t; |
289 | using offset_type = unsigned; |
290 | |
291 | hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Buffer: Key); } |
292 | |
293 | std::pair<unsigned, unsigned> |
294 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) { |
295 | uint32_t KeyLength = Key.size(); |
296 | uint32_t DataLength = sizeof(uint32_t); |
297 | |
298 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
299 | writer.write<uint16_t>(Val: KeyLength); |
300 | writer.write<uint16_t>(Val: DataLength); |
301 | return {KeyLength, DataLength}; |
302 | } |
303 | |
304 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { OS << Key; } |
305 | |
306 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
307 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
308 | writer.write<uint32_t>(Val: Data); |
309 | } |
310 | }; |
311 | } // namespace |
312 | |
313 | void APINotesWriter::Implementation::writeIdentifierBlock( |
314 | llvm::BitstreamWriter &Stream) { |
315 | llvm::BCBlockRAII restoreBlock(Stream, IDENTIFIER_BLOCK_ID, 3); |
316 | |
317 | if (IdentifierIDs.empty()) |
318 | return; |
319 | |
320 | llvm::SmallString<4096> HashTableBlob; |
321 | uint32_t Offset; |
322 | { |
323 | llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> Generator; |
324 | for (auto &II : IdentifierIDs) |
325 | Generator.insert(Key: II.first(), Data: II.second); |
326 | |
327 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
328 | // Make sure that no bucket is at offset 0 |
329 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
330 | endian: llvm::endianness::little); |
331 | Offset = Generator.Emit(Out&: BlobStream); |
332 | } |
333 | |
334 | identifier_block::IdentifierDataLayout IdentifierData(Stream); |
335 | IdentifierData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
336 | } |
337 | |
338 | namespace { |
339 | /// Used to serialize the on-disk Objective-C context table. |
340 | class ObjCContextIDTableInfo { |
341 | public: |
342 | using key_type = ContextTableKey; |
343 | using key_type_ref = key_type; |
344 | using data_type = unsigned; |
345 | using data_type_ref = const data_type &; |
346 | using hash_value_type = size_t; |
347 | using offset_type = unsigned; |
348 | |
349 | hash_value_type ComputeHash(key_type_ref Key) { |
350 | return static_cast<size_t>(Key.hashValue()); |
351 | } |
352 | |
353 | std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &OS, key_type_ref, |
354 | data_type_ref) { |
355 | uint32_t KeyLength = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); |
356 | uint32_t DataLength = sizeof(uint32_t); |
357 | |
358 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
359 | writer.write<uint16_t>(Val: KeyLength); |
360 | writer.write<uint16_t>(Val: DataLength); |
361 | return {KeyLength, DataLength}; |
362 | } |
363 | |
364 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
365 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
366 | writer.write<uint32_t>(Val: Key.parentContextID); |
367 | writer.write<uint8_t>(Val: Key.contextKind); |
368 | writer.write<uint32_t>(Val: Key.contextID); |
369 | } |
370 | |
371 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
372 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
373 | writer.write<uint32_t>(Val: Data); |
374 | } |
375 | }; |
376 | |
377 | /// Localized helper to make a type dependent, thwarting template argument |
378 | /// deduction. |
379 | template <typename T> struct MakeDependent { typedef T Type; }; |
380 | |
381 | /// Retrieve the serialized size of the given VersionTuple, for use in |
382 | /// on-disk hash tables. |
383 | unsigned getVersionTupleSize(const VersionTuple &VT) { |
384 | unsigned size = sizeof(uint8_t) + /*major*/ sizeof(uint32_t); |
385 | if (VT.getMinor()) |
386 | size += sizeof(uint32_t); |
387 | if (VT.getSubminor()) |
388 | size += sizeof(uint32_t); |
389 | if (VT.getBuild()) |
390 | size += sizeof(uint32_t); |
391 | return size; |
392 | } |
393 | |
394 | /// Determine the size of an array of versioned information, |
395 | template <typename T> |
396 | unsigned getVersionedInfoSize( |
397 | const llvm::SmallVectorImpl<std::pair<llvm::VersionTuple, T>> &VI, |
398 | llvm::function_ref<unsigned(const typename MakeDependent<T>::Type &)> |
399 | getInfoSize) { |
400 | unsigned result = sizeof(uint16_t); // # of elements |
401 | for (const auto &E : VI) { |
402 | result += getVersionTupleSize(E.first); |
403 | result += getInfoSize(E.second); |
404 | } |
405 | return result; |
406 | } |
407 | |
408 | /// Emit a serialized representation of a version tuple. |
409 | void emitVersionTuple(raw_ostream &OS, const VersionTuple &VT) { |
410 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
411 | |
412 | // First byte contains the number of components beyond the 'major' component. |
413 | uint8_t descriptor; |
414 | if (VT.getBuild()) |
415 | descriptor = 3; |
416 | else if (VT.getSubminor()) |
417 | descriptor = 2; |
418 | else if (VT.getMinor()) |
419 | descriptor = 1; |
420 | else |
421 | descriptor = 0; |
422 | writer.write<uint8_t>(Val: descriptor); |
423 | |
424 | // Write the components. |
425 | writer.write<uint32_t>(Val: VT.getMajor()); |
426 | if (auto minor = VT.getMinor()) |
427 | writer.write<uint32_t>(Val: *minor); |
428 | if (auto subminor = VT.getSubminor()) |
429 | writer.write<uint32_t>(Val: *subminor); |
430 | if (auto build = VT.getBuild()) |
431 | writer.write<uint32_t>(Val: *build); |
432 | } |
433 | |
434 | /// Emit versioned information. |
435 | template <typename T> |
436 | void emitVersionedInfo( |
437 | raw_ostream &OS, llvm::SmallVectorImpl<std::pair<VersionTuple, T>> &VI, |
438 | llvm::function_ref<void(raw_ostream &, |
439 | const typename MakeDependent<T>::Type &)> |
440 | emitInfo) { |
441 | std::sort(VI.begin(), VI.end(), |
442 | [](const std::pair<VersionTuple, T> &LHS, |
443 | const std::pair<VersionTuple, T> &RHS) -> bool { |
444 | assert((&LHS == &RHS || LHS.first != RHS.first) && |
445 | "two entries for the same version" ); |
446 | return LHS.first < RHS.first; |
447 | }); |
448 | |
449 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
450 | writer.write<uint16_t>(VI.size()); |
451 | for (const auto &E : VI) { |
452 | emitVersionTuple(OS, E.first); |
453 | emitInfo(OS, E.second); |
454 | } |
455 | } |
456 | |
457 | /// On-disk hash table info key base for handling versioned data. |
458 | template <typename Derived, typename KeyType, typename UnversionedDataType> |
459 | class VersionedTableInfo { |
460 | Derived &asDerived() { return *static_cast<Derived *>(this); } |
461 | |
462 | const Derived &asDerived() const { |
463 | return *static_cast<const Derived *>(this); |
464 | } |
465 | |
466 | public: |
467 | using key_type = KeyType; |
468 | using key_type_ref = key_type; |
469 | using data_type = |
470 | llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>; |
471 | using data_type_ref = data_type &; |
472 | using hash_value_type = size_t; |
473 | using offset_type = unsigned; |
474 | |
475 | std::pair<unsigned, unsigned> |
476 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref Data) { |
477 | uint32_t KeyLength = asDerived().getKeyLength(Key); |
478 | uint32_t DataLength = |
479 | getVersionedInfoSize(Data, [this](const UnversionedDataType &UI) { |
480 | return asDerived().getUnversionedInfoSize(UI); |
481 | }); |
482 | |
483 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
484 | writer.write<uint16_t>(Val: KeyLength); |
485 | writer.write<uint16_t>(Val: DataLength); |
486 | return {KeyLength, DataLength}; |
487 | } |
488 | |
489 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
490 | emitVersionedInfo( |
491 | OS, Data, [this](llvm::raw_ostream &OS, const UnversionedDataType &UI) { |
492 | asDerived().emitUnversionedInfo(OS, UI); |
493 | }); |
494 | } |
495 | }; |
496 | |
497 | /// Emit a serialized representation of the common entity information. |
498 | void emitCommonEntityInfo(raw_ostream &OS, const CommonEntityInfo &CEI) { |
499 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
500 | |
501 | uint8_t payload = 0; |
502 | if (auto swiftPrivate = CEI.isSwiftPrivate()) { |
503 | payload |= 0x01; |
504 | if (*swiftPrivate) |
505 | payload |= 0x02; |
506 | } |
507 | payload <<= 1; |
508 | payload |= CEI.Unavailable; |
509 | payload <<= 1; |
510 | payload |= CEI.UnavailableInSwift; |
511 | |
512 | writer.write<uint8_t>(Val: payload); |
513 | |
514 | writer.write<uint16_t>(Val: CEI.UnavailableMsg.size()); |
515 | OS.write(Ptr: CEI.UnavailableMsg.c_str(), Size: CEI.UnavailableMsg.size()); |
516 | |
517 | writer.write<uint16_t>(Val: CEI.SwiftName.size()); |
518 | OS.write(Ptr: CEI.SwiftName.c_str(), Size: CEI.SwiftName.size()); |
519 | } |
520 | |
521 | /// Retrieve the serialized size of the given CommonEntityInfo, for use in |
522 | /// on-disk hash tables. |
523 | unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) { |
524 | return 5 + CEI.UnavailableMsg.size() + CEI.SwiftName.size(); |
525 | } |
526 | |
527 | // Retrieve the serialized size of the given CommonTypeInfo, for use |
528 | // in on-disk hash tables. |
529 | unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) { |
530 | return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 + |
531 | (CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) + |
532 | getCommonEntityInfoSize(CEI: CTI); |
533 | } |
534 | |
535 | /// Emit a serialized representation of the common type information. |
536 | void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) { |
537 | emitCommonEntityInfo(OS, CEI: CTI); |
538 | |
539 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
540 | if (auto swiftBridge = CTI.getSwiftBridge()) { |
541 | writer.write<uint16_t>(Val: swiftBridge->size() + 1); |
542 | OS.write(Ptr: swiftBridge->c_str(), Size: swiftBridge->size()); |
543 | } else { |
544 | writer.write<uint16_t>(Val: 0); |
545 | } |
546 | if (auto nsErrorDomain = CTI.getNSErrorDomain()) { |
547 | writer.write<uint16_t>(Val: nsErrorDomain->size() + 1); |
548 | OS.write(Ptr: nsErrorDomain->c_str(), Size: CTI.getNSErrorDomain()->size()); |
549 | } else { |
550 | writer.write<uint16_t>(Val: 0); |
551 | } |
552 | } |
553 | |
554 | /// Used to serialize the on-disk Objective-C property table. |
555 | class ObjCContextInfoTableInfo |
556 | : public VersionedTableInfo<ObjCContextInfoTableInfo, unsigned, |
557 | ObjCContextInfo> { |
558 | public: |
559 | unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); } |
560 | |
561 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
562 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
563 | writer.write<uint32_t>(Val: Key); |
564 | } |
565 | |
566 | hash_value_type ComputeHash(key_type_ref Key) { |
567 | return static_cast<size_t>(llvm::hash_value(value: Key)); |
568 | } |
569 | |
570 | unsigned getUnversionedInfoSize(const ObjCContextInfo &OCI) { |
571 | return getCommonTypeInfoSize(CTI: OCI) + 1; |
572 | } |
573 | |
574 | void emitUnversionedInfo(raw_ostream &OS, const ObjCContextInfo &OCI) { |
575 | emitCommonTypeInfo(OS, CTI: OCI); |
576 | |
577 | uint8_t payload = 0; |
578 | if (auto swiftImportAsNonGeneric = OCI.getSwiftImportAsNonGeneric()) |
579 | payload |= (0x01 << 1) | (uint8_t)swiftImportAsNonGeneric.value(); |
580 | payload <<= 2; |
581 | if (auto swiftObjCMembers = OCI.getSwiftObjCMembers()) |
582 | payload |= (0x01 << 1) | (uint8_t)swiftObjCMembers.value(); |
583 | payload <<= 3; |
584 | if (auto nullable = OCI.getDefaultNullability()) |
585 | payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable); |
586 | payload = (payload << 1) | (OCI.hasDesignatedInits() ? 1 : 0); |
587 | |
588 | OS << payload; |
589 | } |
590 | }; |
591 | } // namespace |
592 | |
593 | void APINotesWriter::Implementation::writeObjCContextBlock( |
594 | llvm::BitstreamWriter &Stream) { |
595 | llvm::BCBlockRAII restoreBlock(Stream, OBJC_CONTEXT_BLOCK_ID, 3); |
596 | |
597 | if (ObjCContexts.empty()) |
598 | return; |
599 | |
600 | { |
601 | llvm::SmallString<4096> HashTableBlob; |
602 | uint32_t Offset; |
603 | { |
604 | llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> Generator; |
605 | for (auto &OC : ObjCContexts) |
606 | Generator.insert(Key: OC.first, Data: OC.second.first); |
607 | |
608 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
609 | // Make sure that no bucket is at offset 0 |
610 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
611 | endian: llvm::endianness::little); |
612 | Offset = Generator.Emit(Out&: BlobStream); |
613 | } |
614 | |
615 | objc_context_block::ObjCContextIDLayout ObjCContextID(Stream); |
616 | ObjCContextID.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
617 | } |
618 | |
619 | { |
620 | llvm::SmallString<4096> HashTableBlob; |
621 | uint32_t Offset; |
622 | { |
623 | llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo> Generator; |
624 | for (auto &OC : ObjCContexts) |
625 | Generator.insert(Key: OC.second.first, Data&: OC.second.second); |
626 | |
627 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
628 | // Make sure that no bucket is at offset 0 |
629 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
630 | endian: llvm::endianness::little); |
631 | Offset = Generator.Emit(Out&: BlobStream); |
632 | } |
633 | |
634 | objc_context_block::ObjCContextInfoLayout ObjCContextInfo(Stream); |
635 | ObjCContextInfo.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
636 | } |
637 | } |
638 | |
639 | namespace { |
640 | /// Retrieve the serialized size of the given VariableInfo, for use in |
641 | /// on-disk hash tables. |
642 | unsigned getVariableInfoSize(const VariableInfo &VI) { |
643 | return 2 + getCommonEntityInfoSize(CEI: VI) + 2 + VI.getType().size(); |
644 | } |
645 | |
646 | /// Emit a serialized representation of the variable information. |
647 | void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) { |
648 | emitCommonEntityInfo(OS, CEI: VI); |
649 | |
650 | uint8_t bytes[2] = {0, 0}; |
651 | if (auto nullable = VI.getNullability()) { |
652 | bytes[0] = 1; |
653 | bytes[1] = static_cast<uint8_t>(*nullable); |
654 | } else { |
655 | // Nothing to do. |
656 | } |
657 | |
658 | OS.write(Ptr: reinterpret_cast<const char *>(bytes), Size: 2); |
659 | |
660 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
661 | writer.write<uint16_t>(Val: VI.getType().size()); |
662 | OS.write(Ptr: VI.getType().data(), Size: VI.getType().size()); |
663 | } |
664 | |
665 | /// Used to serialize the on-disk Objective-C property table. |
666 | class ObjCPropertyTableInfo |
667 | : public VersionedTableInfo<ObjCPropertyTableInfo, |
668 | std::tuple<unsigned, unsigned, char>, |
669 | ObjCPropertyInfo> { |
670 | public: |
671 | unsigned getKeyLength(key_type_ref) { |
672 | return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); |
673 | } |
674 | |
675 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
676 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
677 | writer.write<uint32_t>(Val: std::get<0>(t&: Key)); |
678 | writer.write<uint32_t>(Val: std::get<1>(t&: Key)); |
679 | writer.write<uint8_t>(Val: std::get<2>(t&: Key)); |
680 | } |
681 | |
682 | hash_value_type ComputeHash(key_type_ref Key) { |
683 | return static_cast<size_t>(llvm::hash_value(arg: Key)); |
684 | } |
685 | |
686 | unsigned getUnversionedInfoSize(const ObjCPropertyInfo &OPI) { |
687 | return getVariableInfoSize(VI: OPI) + 1; |
688 | } |
689 | |
690 | void emitUnversionedInfo(raw_ostream &OS, const ObjCPropertyInfo &OPI) { |
691 | emitVariableInfo(OS, VI: OPI); |
692 | |
693 | uint8_t flags = 0; |
694 | if (auto value = OPI.getSwiftImportAsAccessors()) { |
695 | flags |= 1 << 0; |
696 | flags |= value.value() << 1; |
697 | } |
698 | OS << flags; |
699 | } |
700 | }; |
701 | } // namespace |
702 | |
703 | void APINotesWriter::Implementation::writeObjCPropertyBlock( |
704 | llvm::BitstreamWriter &Stream) { |
705 | llvm::BCBlockRAII Scope(Stream, OBJC_PROPERTY_BLOCK_ID, 3); |
706 | |
707 | if (ObjCProperties.empty()) |
708 | return; |
709 | |
710 | { |
711 | llvm::SmallString<4096> HashTableBlob; |
712 | uint32_t Offset; |
713 | { |
714 | llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> Generator; |
715 | for (auto &OP : ObjCProperties) |
716 | Generator.insert(Key: OP.first, Data&: OP.second); |
717 | |
718 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
719 | // Make sure that no bucket is at offset 0 |
720 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
721 | endian: llvm::endianness::little); |
722 | Offset = Generator.Emit(Out&: BlobStream); |
723 | } |
724 | |
725 | objc_property_block::ObjCPropertyDataLayout ObjCPropertyData(Stream); |
726 | ObjCPropertyData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
727 | } |
728 | } |
729 | |
730 | namespace { |
731 | unsigned getFunctionInfoSize(const FunctionInfo &); |
732 | void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &); |
733 | |
734 | /// Used to serialize the on-disk Objective-C method table. |
735 | class ObjCMethodTableInfo |
736 | : public VersionedTableInfo<ObjCMethodTableInfo, |
737 | std::tuple<unsigned, unsigned, char>, |
738 | ObjCMethodInfo> { |
739 | public: |
740 | unsigned getKeyLength(key_type_ref) { |
741 | return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); |
742 | } |
743 | |
744 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
745 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
746 | writer.write<uint32_t>(Val: std::get<0>(t&: Key)); |
747 | writer.write<uint32_t>(Val: std::get<1>(t&: Key)); |
748 | writer.write<uint8_t>(Val: std::get<2>(t&: Key)); |
749 | } |
750 | |
751 | hash_value_type ComputeHash(key_type_ref key) { |
752 | return static_cast<size_t>(llvm::hash_value(arg: key)); |
753 | } |
754 | |
755 | unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) { |
756 | return getFunctionInfoSize(OMI) + 1; |
757 | } |
758 | |
759 | void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) { |
760 | uint8_t flags = 0; |
761 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
762 | flags = (flags << 1) | OMI.DesignatedInit; |
763 | flags = (flags << 1) | OMI.RequiredInit; |
764 | writer.write<uint8_t>(Val: flags); |
765 | |
766 | emitFunctionInfo(OS, OMI); |
767 | } |
768 | }; |
769 | } // namespace |
770 | |
771 | void APINotesWriter::Implementation::writeObjCMethodBlock( |
772 | llvm::BitstreamWriter &Stream) { |
773 | llvm::BCBlockRAII Scope(Stream, OBJC_METHOD_BLOCK_ID, 3); |
774 | |
775 | if (ObjCMethods.empty()) |
776 | return; |
777 | |
778 | { |
779 | llvm::SmallString<4096> HashTableBlob; |
780 | uint32_t Offset; |
781 | { |
782 | llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> Generator; |
783 | for (auto &OM : ObjCMethods) |
784 | Generator.insert(Key: OM.first, Data&: OM.second); |
785 | |
786 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
787 | // Make sure that no bucket is at offset 0 |
788 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
789 | endian: llvm::endianness::little); |
790 | Offset = Generator.Emit(Out&: BlobStream); |
791 | } |
792 | |
793 | objc_method_block::ObjCMethodDataLayout ObjCMethodData(Stream); |
794 | ObjCMethodData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
795 | } |
796 | } |
797 | |
798 | namespace { |
799 | /// Used to serialize the on-disk Objective-C selector table. |
800 | class ObjCSelectorTableInfo { |
801 | public: |
802 | using key_type = StoredObjCSelector; |
803 | using key_type_ref = const key_type &; |
804 | using data_type = SelectorID; |
805 | using data_type_ref = data_type; |
806 | using hash_value_type = unsigned; |
807 | using offset_type = unsigned; |
808 | |
809 | hash_value_type ComputeHash(key_type_ref Key) { |
810 | return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Selector: Key); |
811 | } |
812 | |
813 | std::pair<unsigned, unsigned> |
814 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) { |
815 | uint32_t KeyLength = |
816 | sizeof(uint16_t) + sizeof(uint32_t) * Key.Identifiers.size(); |
817 | uint32_t DataLength = sizeof(uint32_t); |
818 | |
819 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
820 | writer.write<uint16_t>(Val: KeyLength); |
821 | writer.write<uint16_t>(Val: DataLength); |
822 | return {KeyLength, DataLength}; |
823 | } |
824 | |
825 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
826 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
827 | writer.write<uint16_t>(Val: Key.NumArgs); |
828 | for (auto Identifier : Key.Identifiers) |
829 | writer.write<uint32_t>(Val: Identifier); |
830 | } |
831 | |
832 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
833 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
834 | writer.write<uint32_t>(Val: Data); |
835 | } |
836 | }; |
837 | } // namespace |
838 | |
839 | void APINotesWriter::Implementation::writeObjCSelectorBlock( |
840 | llvm::BitstreamWriter &Stream) { |
841 | llvm::BCBlockRAII Scope(Stream, OBJC_SELECTOR_BLOCK_ID, 3); |
842 | |
843 | if (SelectorIDs.empty()) |
844 | return; |
845 | |
846 | { |
847 | llvm::SmallString<4096> HashTableBlob; |
848 | uint32_t Offset; |
849 | { |
850 | llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> Generator; |
851 | for (auto &S : SelectorIDs) |
852 | Generator.insert(Key: S.first, Data: S.second); |
853 | |
854 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
855 | // Make sure that no bucket is at offset 0 |
856 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
857 | endian: llvm::endianness::little); |
858 | Offset = Generator.Emit(Out&: BlobStream); |
859 | } |
860 | |
861 | objc_selector_block::ObjCSelectorDataLayout ObjCSelectorData(Stream); |
862 | ObjCSelectorData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
863 | } |
864 | } |
865 | |
866 | namespace { |
867 | /// Used to serialize the on-disk global variable table. |
868 | class GlobalVariableTableInfo |
869 | : public VersionedTableInfo<GlobalVariableTableInfo, ContextTableKey, |
870 | GlobalVariableInfo> { |
871 | public: |
872 | unsigned getKeyLength(key_type_ref) { |
873 | return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); |
874 | } |
875 | |
876 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
877 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
878 | writer.write<uint32_t>(Val: Key.parentContextID); |
879 | writer.write<uint8_t>(Val: Key.contextKind); |
880 | writer.write<uint32_t>(Val: Key.contextID); |
881 | } |
882 | |
883 | hash_value_type ComputeHash(key_type_ref Key) { |
884 | return static_cast<size_t>(Key.hashValue()); |
885 | } |
886 | |
887 | unsigned getUnversionedInfoSize(const GlobalVariableInfo &GVI) { |
888 | return getVariableInfoSize(VI: GVI); |
889 | } |
890 | |
891 | void emitUnversionedInfo(raw_ostream &OS, const GlobalVariableInfo &GVI) { |
892 | emitVariableInfo(OS, VI: GVI); |
893 | } |
894 | }; |
895 | } // namespace |
896 | |
897 | void APINotesWriter::Implementation::writeGlobalVariableBlock( |
898 | llvm::BitstreamWriter &Stream) { |
899 | llvm::BCBlockRAII Scope(Stream, GLOBAL_VARIABLE_BLOCK_ID, 3); |
900 | |
901 | if (GlobalVariables.empty()) |
902 | return; |
903 | |
904 | { |
905 | llvm::SmallString<4096> HashTableBlob; |
906 | uint32_t Offset; |
907 | { |
908 | llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> Generator; |
909 | for (auto &GV : GlobalVariables) |
910 | Generator.insert(Key: GV.first, Data&: GV.second); |
911 | |
912 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
913 | // Make sure that no bucket is at offset 0 |
914 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
915 | endian: llvm::endianness::little); |
916 | Offset = Generator.Emit(Out&: BlobStream); |
917 | } |
918 | |
919 | global_variable_block::GlobalVariableDataLayout GlobalVariableData(Stream); |
920 | GlobalVariableData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
921 | } |
922 | } |
923 | |
924 | namespace { |
925 | unsigned getParamInfoSize(const ParamInfo &PI) { |
926 | return getVariableInfoSize(VI: PI) + 1; |
927 | } |
928 | |
929 | void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) { |
930 | emitVariableInfo(OS, VI: PI); |
931 | |
932 | uint8_t flags = 0; |
933 | if (auto noescape = PI.isNoEscape()) { |
934 | flags |= 0x01; |
935 | if (*noescape) |
936 | flags |= 0x02; |
937 | } |
938 | flags <<= 3; |
939 | if (auto RCC = PI.getRetainCountConvention()) |
940 | flags |= static_cast<uint8_t>(RCC.value()) + 1; |
941 | |
942 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
943 | writer.write<uint8_t>(Val: flags); |
944 | } |
945 | |
946 | /// Retrieve the serialized size of the given FunctionInfo, for use in on-disk |
947 | /// hash tables. |
948 | unsigned getFunctionInfoSize(const FunctionInfo &FI) { |
949 | unsigned size = getCommonEntityInfoSize(CEI: FI) + 2 + sizeof(uint64_t); |
950 | size += sizeof(uint16_t); |
951 | for (const auto &P : FI.Params) |
952 | size += getParamInfoSize(PI: P); |
953 | size += sizeof(uint16_t) + FI.ResultType.size(); |
954 | return size; |
955 | } |
956 | |
957 | /// Emit a serialized representation of the function information. |
958 | void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) { |
959 | emitCommonEntityInfo(OS, CEI: FI); |
960 | |
961 | uint8_t flags = 0; |
962 | flags |= FI.NullabilityAudited; |
963 | flags <<= 3; |
964 | if (auto RCC = FI.getRetainCountConvention()) |
965 | flags |= static_cast<uint8_t>(RCC.value()) + 1; |
966 | |
967 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
968 | |
969 | writer.write<uint8_t>(Val: flags); |
970 | writer.write<uint8_t>(Val: FI.NumAdjustedNullable); |
971 | writer.write<uint64_t>(Val: FI.NullabilityPayload); |
972 | |
973 | writer.write<uint16_t>(Val: FI.Params.size()); |
974 | for (const auto &PI : FI.Params) |
975 | emitParamInfo(OS, PI); |
976 | |
977 | writer.write<uint16_t>(Val: FI.ResultType.size()); |
978 | writer.write(Val: ArrayRef<char>{FI.ResultType.data(), FI.ResultType.size()}); |
979 | } |
980 | |
981 | /// Used to serialize the on-disk global function table. |
982 | class GlobalFunctionTableInfo |
983 | : public VersionedTableInfo<GlobalFunctionTableInfo, ContextTableKey, |
984 | GlobalFunctionInfo> { |
985 | public: |
986 | unsigned getKeyLength(key_type_ref) { |
987 | return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); |
988 | } |
989 | |
990 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
991 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
992 | writer.write<uint32_t>(Val: Key.parentContextID); |
993 | writer.write<uint8_t>(Val: Key.contextKind); |
994 | writer.write<uint32_t>(Val: Key.contextID); |
995 | } |
996 | |
997 | hash_value_type ComputeHash(key_type_ref Key) { |
998 | return static_cast<size_t>(Key.hashValue()); |
999 | } |
1000 | |
1001 | unsigned getUnversionedInfoSize(const GlobalFunctionInfo &GFI) { |
1002 | return getFunctionInfoSize(FI: GFI); |
1003 | } |
1004 | |
1005 | void emitUnversionedInfo(raw_ostream &OS, const GlobalFunctionInfo &GFI) { |
1006 | emitFunctionInfo(OS, FI: GFI); |
1007 | } |
1008 | }; |
1009 | } // namespace |
1010 | |
1011 | void APINotesWriter::Implementation::writeGlobalFunctionBlock( |
1012 | llvm::BitstreamWriter &Stream) { |
1013 | llvm::BCBlockRAII Scope(Stream, GLOBAL_FUNCTION_BLOCK_ID, 3); |
1014 | |
1015 | if (GlobalFunctions.empty()) |
1016 | return; |
1017 | |
1018 | { |
1019 | llvm::SmallString<4096> HashTableBlob; |
1020 | uint32_t Offset; |
1021 | { |
1022 | llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> Generator; |
1023 | for (auto &F : GlobalFunctions) |
1024 | Generator.insert(Key: F.first, Data&: F.second); |
1025 | |
1026 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1027 | // Make sure that no bucket is at offset 0 |
1028 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1029 | endian: llvm::endianness::little); |
1030 | Offset = Generator.Emit(Out&: BlobStream); |
1031 | } |
1032 | |
1033 | global_function_block::GlobalFunctionDataLayout GlobalFunctionData(Stream); |
1034 | GlobalFunctionData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1035 | } |
1036 | } |
1037 | |
1038 | namespace { |
1039 | /// Used to serialize the on-disk global enum constant. |
1040 | class EnumConstantTableInfo |
1041 | : public VersionedTableInfo<EnumConstantTableInfo, unsigned, |
1042 | EnumConstantInfo> { |
1043 | public: |
1044 | unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); } |
1045 | |
1046 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
1047 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1048 | writer.write<uint32_t>(Val: Key); |
1049 | } |
1050 | |
1051 | hash_value_type ComputeHash(key_type_ref Key) { |
1052 | return static_cast<size_t>(llvm::hash_value(value: Key)); |
1053 | } |
1054 | |
1055 | unsigned getUnversionedInfoSize(const EnumConstantInfo &ECI) { |
1056 | return getCommonEntityInfoSize(CEI: ECI); |
1057 | } |
1058 | |
1059 | void emitUnversionedInfo(raw_ostream &OS, const EnumConstantInfo &ECI) { |
1060 | emitCommonEntityInfo(OS, CEI: ECI); |
1061 | } |
1062 | }; |
1063 | } // namespace |
1064 | |
1065 | void APINotesWriter::Implementation::writeEnumConstantBlock( |
1066 | llvm::BitstreamWriter &Stream) { |
1067 | llvm::BCBlockRAII Scope(Stream, ENUM_CONSTANT_BLOCK_ID, 3); |
1068 | |
1069 | if (EnumConstants.empty()) |
1070 | return; |
1071 | |
1072 | { |
1073 | llvm::SmallString<4096> HashTableBlob; |
1074 | uint32_t Offset; |
1075 | { |
1076 | llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> Generator; |
1077 | for (auto &EC : EnumConstants) |
1078 | Generator.insert(Key: EC.first, Data&: EC.second); |
1079 | |
1080 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1081 | // Make sure that no bucket is at offset 0 |
1082 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1083 | endian: llvm::endianness::little); |
1084 | Offset = Generator.Emit(Out&: BlobStream); |
1085 | } |
1086 | |
1087 | enum_constant_block::EnumConstantDataLayout EnumConstantData(Stream); |
1088 | EnumConstantData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1089 | } |
1090 | } |
1091 | |
1092 | namespace { |
1093 | template <typename Derived, typename UnversionedDataType> |
1094 | class CommonTypeTableInfo |
1095 | : public VersionedTableInfo<Derived, ContextTableKey, UnversionedDataType> { |
1096 | public: |
1097 | using key_type_ref = typename CommonTypeTableInfo::key_type_ref; |
1098 | using hash_value_type = typename CommonTypeTableInfo::hash_value_type; |
1099 | |
1100 | unsigned getKeyLength(key_type_ref) { |
1101 | return sizeof(uint32_t) + sizeof(uint8_t) + sizeof(IdentifierID); |
1102 | } |
1103 | |
1104 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
1105 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1106 | writer.write<uint32_t>(Key.parentContextID); |
1107 | writer.write<uint8_t>(Key.contextKind); |
1108 | writer.write<IdentifierID>(Key.contextID); |
1109 | } |
1110 | |
1111 | hash_value_type ComputeHash(key_type_ref Key) { |
1112 | return static_cast<size_t>(Key.hashValue()); |
1113 | } |
1114 | |
1115 | unsigned getUnversionedInfoSize(const UnversionedDataType &UDT) { |
1116 | return getCommonTypeInfoSize(UDT); |
1117 | } |
1118 | |
1119 | void emitUnversionedInfo(raw_ostream &OS, const UnversionedDataType &UDT) { |
1120 | emitCommonTypeInfo(OS, UDT); |
1121 | } |
1122 | }; |
1123 | |
1124 | /// Used to serialize the on-disk tag table. |
1125 | class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { |
1126 | public: |
1127 | unsigned getUnversionedInfoSize(const TagInfo &TI) { |
1128 | return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + |
1129 | 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + |
1130 | 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + |
1131 | 1 + getCommonTypeInfoSize(CTI: TI); |
1132 | } |
1133 | |
1134 | void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) { |
1135 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1136 | |
1137 | uint8_t Flags = 0; |
1138 | if (auto extensibility = TI.EnumExtensibility) { |
1139 | Flags |= static_cast<uint8_t>(extensibility.value()) + 1; |
1140 | assert((Flags < (1 << 2)) && "must fit in two bits" ); |
1141 | } |
1142 | |
1143 | Flags <<= 2; |
1144 | if (auto value = TI.isFlagEnum()) |
1145 | Flags |= (value.value() << 1 | 1 << 0); |
1146 | |
1147 | writer.write<uint8_t>(Val: Flags); |
1148 | |
1149 | if (auto ImportAs = TI.SwiftImportAs) { |
1150 | writer.write<uint16_t>(Val: ImportAs->size() + 1); |
1151 | OS.write(Ptr: ImportAs->c_str(), Size: ImportAs->size()); |
1152 | } else { |
1153 | writer.write<uint16_t>(Val: 0); |
1154 | } |
1155 | if (auto RetainOp = TI.SwiftRetainOp) { |
1156 | writer.write<uint16_t>(Val: RetainOp->size() + 1); |
1157 | OS.write(Ptr: RetainOp->c_str(), Size: RetainOp->size()); |
1158 | } else { |
1159 | writer.write<uint16_t>(Val: 0); |
1160 | } |
1161 | if (auto ReleaseOp = TI.SwiftReleaseOp) { |
1162 | writer.write<uint16_t>(Val: ReleaseOp->size() + 1); |
1163 | OS.write(Ptr: ReleaseOp->c_str(), Size: ReleaseOp->size()); |
1164 | } else { |
1165 | writer.write<uint16_t>(Val: 0); |
1166 | } |
1167 | |
1168 | emitCommonTypeInfo(OS, CTI: TI); |
1169 | } |
1170 | }; |
1171 | } // namespace |
1172 | |
1173 | void APINotesWriter::Implementation::writeTagBlock( |
1174 | llvm::BitstreamWriter &Stream) { |
1175 | llvm::BCBlockRAII Scope(Stream, TAG_BLOCK_ID, 3); |
1176 | |
1177 | if (Tags.empty()) |
1178 | return; |
1179 | |
1180 | { |
1181 | llvm::SmallString<4096> HashTableBlob; |
1182 | uint32_t Offset; |
1183 | { |
1184 | llvm::OnDiskChainedHashTableGenerator<TagTableInfo> Generator; |
1185 | for (auto &T : Tags) |
1186 | Generator.insert(Key: T.first, Data&: T.second); |
1187 | |
1188 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1189 | // Make sure that no bucket is at offset 0 |
1190 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1191 | endian: llvm::endianness::little); |
1192 | Offset = Generator.Emit(Out&: BlobStream); |
1193 | } |
1194 | |
1195 | tag_block::TagDataLayout TagData(Stream); |
1196 | TagData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1197 | } |
1198 | } |
1199 | |
1200 | namespace { |
1201 | /// Used to serialize the on-disk typedef table. |
1202 | class TypedefTableInfo |
1203 | : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> { |
1204 | public: |
1205 | unsigned getUnversionedInfoSize(const TypedefInfo &TI) { |
1206 | return 1 + getCommonTypeInfoSize(CTI: TI); |
1207 | } |
1208 | |
1209 | void emitUnversionedInfo(raw_ostream &OS, const TypedefInfo &TI) { |
1210 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1211 | |
1212 | uint8_t Flags = 0; |
1213 | if (auto swiftWrapper = TI.SwiftWrapper) |
1214 | Flags |= static_cast<uint8_t>(*swiftWrapper) + 1; |
1215 | |
1216 | writer.write<uint8_t>(Val: Flags); |
1217 | |
1218 | emitCommonTypeInfo(OS, CTI: TI); |
1219 | } |
1220 | }; |
1221 | } // namespace |
1222 | |
1223 | void APINotesWriter::Implementation::writeTypedefBlock( |
1224 | llvm::BitstreamWriter &Stream) { |
1225 | llvm::BCBlockRAII Scope(Stream, TYPEDEF_BLOCK_ID, 3); |
1226 | |
1227 | if (Typedefs.empty()) |
1228 | return; |
1229 | |
1230 | { |
1231 | llvm::SmallString<4096> HashTableBlob; |
1232 | uint32_t Offset; |
1233 | { |
1234 | llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> Generator; |
1235 | for (auto &T : Typedefs) |
1236 | Generator.insert(Key: T.first, Data&: T.second); |
1237 | |
1238 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1239 | // Make sure that no bucket is at offset 0 |
1240 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1241 | endian: llvm::endianness::little); |
1242 | Offset = Generator.Emit(Out&: BlobStream); |
1243 | } |
1244 | |
1245 | typedef_block::TypedefDataLayout TypedefData(Stream); |
1246 | TypedefData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1247 | } |
1248 | } |
1249 | |
1250 | // APINotesWriter |
1251 | |
1252 | APINotesWriter::APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF) |
1253 | : Implementation(new class Implementation(ModuleName, SF)) {} |
1254 | |
1255 | APINotesWriter::~APINotesWriter() = default; |
1256 | |
1257 | void APINotesWriter::writeToStream(llvm::raw_ostream &OS) { |
1258 | Implementation->writeToStream(OS); |
1259 | } |
1260 | |
1261 | ContextID APINotesWriter::addObjCContext(std::optional<ContextID> ParentCtxID, |
1262 | StringRef Name, ContextKind Kind, |
1263 | const ObjCContextInfo &Info, |
1264 | VersionTuple SwiftVersion) { |
1265 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1266 | |
1267 | uint32_t RawParentCtxID = ParentCtxID ? ParentCtxID->Value : -1; |
1268 | ContextTableKey Key(RawParentCtxID, static_cast<uint8_t>(Kind), NameID); |
1269 | auto Known = Implementation->ObjCContexts.find(Val: Key); |
1270 | if (Known == Implementation->ObjCContexts.end()) { |
1271 | unsigned NextID = Implementation->ObjCContexts.size() + 1; |
1272 | |
1273 | Implementation::VersionedSmallVector<ObjCContextInfo> EmptyVersionedInfo; |
1274 | Known = Implementation->ObjCContexts |
1275 | .insert(KV: std::make_pair( |
1276 | x&: Key, y: std::make_pair(x&: NextID, y&: EmptyVersionedInfo))) |
1277 | .first; |
1278 | |
1279 | Implementation->ObjCContextNames[NextID] = NameID; |
1280 | Implementation->ParentContexts[NextID] = RawParentCtxID; |
1281 | } |
1282 | |
1283 | // Add this version information. |
1284 | auto &VersionedVec = Known->second.second; |
1285 | bool Found = false; |
1286 | for (auto &Versioned : VersionedVec) { |
1287 | if (Versioned.first == SwiftVersion) { |
1288 | Versioned.second |= Info; |
1289 | Found = true; |
1290 | break; |
1291 | } |
1292 | } |
1293 | |
1294 | if (!Found) |
1295 | VersionedVec.push_back(Elt: {SwiftVersion, Info}); |
1296 | |
1297 | return ContextID(Known->second.first); |
1298 | } |
1299 | |
1300 | void APINotesWriter::addObjCProperty(ContextID CtxID, StringRef Name, |
1301 | bool IsInstanceProperty, |
1302 | const ObjCPropertyInfo &Info, |
1303 | VersionTuple SwiftVersion) { |
1304 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1305 | Implementation |
1306 | ->ObjCProperties[std::make_tuple(args&: CtxID.Value, args&: NameID, args&: IsInstanceProperty)] |
1307 | .push_back(Elt: {SwiftVersion, Info}); |
1308 | } |
1309 | |
1310 | void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, |
1311 | bool IsInstanceMethod, |
1312 | const ObjCMethodInfo &Info, |
1313 | VersionTuple SwiftVersion) { |
1314 | SelectorID SelID = Implementation->getSelector(SelectorRef: Selector); |
1315 | auto Key = std::tuple<unsigned, unsigned, char>{CtxID.Value, SelID, |
1316 | IsInstanceMethod}; |
1317 | Implementation->ObjCMethods[Key].push_back(Elt: {SwiftVersion, Info}); |
1318 | |
1319 | // If this method is a designated initializer, update the class to note that |
1320 | // it has designated initializers. |
1321 | if (Info.DesignatedInit) { |
1322 | assert(Implementation->ParentContexts.contains(CtxID.Value)); |
1323 | uint32_t ParentCtxID = Implementation->ParentContexts[CtxID.Value]; |
1324 | ContextTableKey CtxKey(ParentCtxID, |
1325 | static_cast<uint8_t>(ContextKind::ObjCClass), |
1326 | Implementation->ObjCContextNames[CtxID.Value]); |
1327 | assert(Implementation->ObjCContexts.contains(CtxKey)); |
1328 | auto &VersionedVec = Implementation->ObjCContexts[CtxKey].second; |
1329 | bool Found = false; |
1330 | for (auto &Versioned : VersionedVec) { |
1331 | if (Versioned.first == SwiftVersion) { |
1332 | Versioned.second.setHasDesignatedInits(true); |
1333 | Found = true; |
1334 | break; |
1335 | } |
1336 | } |
1337 | |
1338 | if (!Found) { |
1339 | VersionedVec.push_back(Elt: {SwiftVersion, ObjCContextInfo()}); |
1340 | VersionedVec.back().second.setHasDesignatedInits(true); |
1341 | } |
1342 | } |
1343 | } |
1344 | |
1345 | void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx, |
1346 | llvm::StringRef Name, |
1347 | const GlobalVariableInfo &Info, |
1348 | VersionTuple SwiftVersion) { |
1349 | IdentifierID VariableID = Implementation->getIdentifier(Identifier: Name); |
1350 | ContextTableKey Key(Ctx, VariableID); |
1351 | Implementation->GlobalVariables[Key].push_back(Elt: {SwiftVersion, Info}); |
1352 | } |
1353 | |
1354 | void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx, |
1355 | llvm::StringRef Name, |
1356 | const GlobalFunctionInfo &Info, |
1357 | VersionTuple SwiftVersion) { |
1358 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1359 | ContextTableKey Key(Ctx, NameID); |
1360 | Implementation->GlobalFunctions[Key].push_back(Elt: {SwiftVersion, Info}); |
1361 | } |
1362 | |
1363 | void APINotesWriter::addEnumConstant(llvm::StringRef Name, |
1364 | const EnumConstantInfo &Info, |
1365 | VersionTuple SwiftVersion) { |
1366 | IdentifierID EnumConstantID = Implementation->getIdentifier(Identifier: Name); |
1367 | Implementation->EnumConstants[EnumConstantID].push_back(Elt: {SwiftVersion, Info}); |
1368 | } |
1369 | |
1370 | void APINotesWriter::addTag(std::optional<Context> Ctx, llvm::StringRef Name, |
1371 | const TagInfo &Info, VersionTuple SwiftVersion) { |
1372 | IdentifierID TagID = Implementation->getIdentifier(Identifier: Name); |
1373 | ContextTableKey Key(Ctx, TagID); |
1374 | Implementation->Tags[Key].push_back(Elt: {SwiftVersion, Info}); |
1375 | } |
1376 | |
1377 | void APINotesWriter::addTypedef(std::optional<Context> Ctx, |
1378 | llvm::StringRef Name, const TypedefInfo &Info, |
1379 | VersionTuple SwiftVersion) { |
1380 | IdentifierID TypedefID = Implementation->getIdentifier(Identifier: Name); |
1381 | ContextTableKey Key(Ctx, TypedefID); |
1382 | Implementation->Typedefs[Key].push_back(Elt: {SwiftVersion, Info}); |
1383 | } |
1384 | } // namespace api_notes |
1385 | } // namespace clang |
1386 | |