1 | //==--- AbstractBasicWriter.h - Abstract basic value serialization --------===// |
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 | #ifndef LLVM_CLANG_AST_ABSTRACTBASICWRITER_H |
10 | #define LLVM_CLANG_AST_ABSTRACTBASICWRITER_H |
11 | |
12 | #include "clang/AST/ASTContext.h" |
13 | #include "clang/AST/DeclTemplate.h" |
14 | #include <optional> |
15 | |
16 | namespace clang { |
17 | namespace serialization { |
18 | |
19 | template <class T> |
20 | inline std::optional<T> makeOptionalFromNullable(const T &value) { |
21 | return (value.isNull() ? std::optional<T>() : std::optional<T>(value)); |
22 | } |
23 | |
24 | template <class T> inline std::optional<T *> makeOptionalFromPointer(T *value) { |
25 | return (value ? std::optional<T *>(value) : std::optional<T *>()); |
26 | } |
27 | |
28 | // PropertyWriter is a class concept that requires the following method: |
29 | // BasicWriter find(llvm::StringRef propertyName); |
30 | // where BasicWriter is some class conforming to the BasicWriter concept. |
31 | // An abstract AST-node writer is created with a PropertyWriter and |
32 | // performs a sequence of calls like so: |
33 | // propertyWriter.find(propertyName).write##TypeName(value) |
34 | // to write the properties of the node it is serializing. |
35 | |
36 | // BasicWriter is a class concept that requires methods like: |
37 | // void write##TypeName(ValueType value); |
38 | // where TypeName is the name of a PropertyType node from PropertiesBase.td |
39 | // and ValueType is the corresponding C++ type name. |
40 | // |
41 | // In addition to the concrete property types, BasicWriter is expected |
42 | // to implement these methods: |
43 | // |
44 | // template <class EnumType> |
45 | // void writeEnum(T value); |
46 | // |
47 | // Writes an enum value as the current property. EnumType will always |
48 | // be an enum type. Only necessary if the BasicWriter doesn't provide |
49 | // type-specific writers for all the enum types. |
50 | // |
51 | // template <class ValueType> |
52 | // void writeOptional(std::optional<ValueType> value); |
53 | // |
54 | // Writes an optional value as the current property. |
55 | // |
56 | // template <class ValueType> |
57 | // void writeArray(ArrayRef<ValueType> value); |
58 | // |
59 | // Writes an array of values as the current property. |
60 | // |
61 | // PropertyWriter writeObject(); |
62 | // |
63 | // Writes an object as the current property; the returned property |
64 | // writer will be subjected to a sequence of property writes and then |
65 | // discarded before any other properties are written to the "outer" |
66 | // property writer (which need not be the same type). The sub-writer |
67 | // will be used as if with the following code: |
68 | // |
69 | // { |
70 | // auto &&widget = W.find("widget").writeObject(); |
71 | // widget.find("kind").writeWidgetKind(...); |
72 | // widget.find("declaration").writeDeclRef(...); |
73 | // } |
74 | |
75 | // WriteDispatcher is a template which does type-based forwarding to one |
76 | // of the write methods of the BasicWriter passed in: |
77 | // |
78 | // template <class ValueType> |
79 | // struct WriteDispatcher { |
80 | // template <class BasicWriter> |
81 | // static void write(BasicWriter &W, ValueType value); |
82 | // }; |
83 | |
84 | // BasicWriterBase provides convenience implementations of the write |
85 | // methods for EnumPropertyType and SubclassPropertyType types that just |
86 | // defer to the "underlying" implementations (for UInt32 and the base class, |
87 | // respectively). |
88 | // |
89 | // template <class Impl> |
90 | // class BasicWriterBase { |
91 | // protected: |
92 | // Impl &asImpl(); |
93 | // public: |
94 | // ... |
95 | // }; |
96 | |
97 | // The actual classes are auto-generated; see ClangASTPropertiesEmitter.cpp. |
98 | #include "clang/AST/AbstractBasicWriter.inc" |
99 | |
100 | /// DataStreamBasicWriter provides convenience implementations for many |
101 | /// BasicWriter methods based on the assumption that the |
102 | /// ultimate writer implementation is based on a variable-length stream |
103 | /// of unstructured data (like Clang's module files). It is designed |
104 | /// to pair with DataStreamBasicReader. |
105 | /// |
106 | /// This class can also act as a PropertyWriter, implementing find("...") |
107 | /// by simply forwarding to itself. |
108 | /// |
109 | /// Unimplemented methods: |
110 | /// writeBool |
111 | /// writeUInt32 |
112 | /// writeUInt64 |
113 | /// writeIdentifier |
114 | /// writeSelector |
115 | /// writeSourceLocation |
116 | /// writeQualType |
117 | /// writeStmtRef |
118 | /// writeDeclRef |
119 | template <class Impl> |
120 | class DataStreamBasicWriter : public BasicWriterBase<Impl> { |
121 | protected: |
122 | using BasicWriterBase<Impl>::asImpl; |
123 | DataStreamBasicWriter(ASTContext &ctx) : BasicWriterBase<Impl>(ctx) {} |
124 | |
125 | public: |
126 | /// Implement property-find by ignoring it. We rely on properties being |
127 | /// serialized and deserialized in a reliable order instead. |
128 | Impl &find(const char *propertyName) { |
129 | return asImpl(); |
130 | } |
131 | |
132 | // Implement object writing by forwarding to this, collapsing the |
133 | // structure into a single data stream. |
134 | Impl &writeObject() { return asImpl(); } |
135 | |
136 | template <class T> |
137 | void writeEnum(T value) { |
138 | asImpl().writeUInt32(uint32_t(value)); |
139 | } |
140 | |
141 | template <class T> |
142 | void writeArray(llvm::ArrayRef<T> array) { |
143 | asImpl().writeUInt32(array.size()); |
144 | for (const T &elt : array) { |
145 | WriteDispatcher<T>::write(asImpl(), elt); |
146 | } |
147 | } |
148 | |
149 | template <class T> void writeOptional(std::optional<T> value) { |
150 | WriteDispatcher<T>::write(asImpl(), PackOptionalValue<T>::pack(value)); |
151 | } |
152 | |
153 | void writeAPSInt(const llvm::APSInt &value) { |
154 | asImpl().writeBool(value.isUnsigned()); |
155 | asImpl().writeAPInt(value); |
156 | } |
157 | |
158 | void writeAPInt(const llvm::APInt &value) { |
159 | asImpl().writeUInt32(value.getBitWidth()); |
160 | const uint64_t *words = value.getRawData(); |
161 | for (size_t i = 0, e = value.getNumWords(); i != e; ++i) |
162 | asImpl().writeUInt64(words[i]); |
163 | } |
164 | |
165 | void writeFixedPointSemantics(const llvm::FixedPointSemantics &sema) { |
166 | asImpl().writeUInt32(sema.getWidth()); |
167 | asImpl().writeUInt32(sema.getScale()); |
168 | asImpl().writeUInt32(sema.isSigned() | sema.isSaturated() << 1 | |
169 | sema.hasUnsignedPadding() << 2); |
170 | } |
171 | |
172 | void writeLValuePathSerializationHelper( |
173 | APValue::LValuePathSerializationHelper lvaluePath) { |
174 | ArrayRef<APValue::LValuePathEntry> path = lvaluePath.Path; |
175 | QualType elemTy = lvaluePath.getType(); |
176 | asImpl().writeQualType(elemTy); |
177 | asImpl().writeUInt32(path.size()); |
178 | auto &ctx = ((BasicWriterBase<Impl> *)this)->getASTContext(); |
179 | for (auto elem : path) { |
180 | if (elemTy->getAs<RecordType>()) { |
181 | asImpl().writeUInt32(elem.getAsBaseOrMember().getInt()); |
182 | const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer(); |
183 | if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) { |
184 | asImpl().writeDeclRef(recordDecl); |
185 | elemTy = ctx.getRecordType(recordDecl); |
186 | } else { |
187 | const auto *valueDecl = cast<ValueDecl>(baseOrMember); |
188 | asImpl().writeDeclRef(valueDecl); |
189 | elemTy = valueDecl->getType(); |
190 | } |
191 | } else { |
192 | asImpl().writeUInt32(elem.getAsArrayIndex()); |
193 | elemTy = ctx.getAsArrayType(elemTy)->getElementType(); |
194 | } |
195 | } |
196 | } |
197 | |
198 | void writeQualifiers(Qualifiers value) { |
199 | static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint32_t), |
200 | "update this if the value size changes" ); |
201 | asImpl().writeUInt32(value.getAsOpaqueValue()); |
202 | } |
203 | |
204 | void writeExceptionSpecInfo( |
205 | const FunctionProtoType::ExceptionSpecInfo &esi) { |
206 | asImpl().writeUInt32(uint32_t(esi.Type)); |
207 | if (esi.Type == EST_Dynamic) { |
208 | asImpl().writeArray(esi.Exceptions); |
209 | } else if (isComputedNoexcept(ESpecType: esi.Type)) { |
210 | asImpl().writeExprRef(esi.NoexceptExpr); |
211 | } else if (esi.Type == EST_Uninstantiated) { |
212 | asImpl().writeDeclRef(esi.SourceDecl); |
213 | asImpl().writeDeclRef(esi.SourceTemplate); |
214 | } else if (esi.Type == EST_Unevaluated) { |
215 | asImpl().writeDeclRef(esi.SourceDecl); |
216 | } |
217 | } |
218 | |
219 | void writeExtParameterInfo(FunctionProtoType::ExtParameterInfo epi) { |
220 | static_assert(sizeof(epi.getOpaqueValue()) <= sizeof(uint32_t), |
221 | "opaque value doesn't fit into uint32_t" ); |
222 | asImpl().writeUInt32(epi.getOpaqueValue()); |
223 | } |
224 | |
225 | void writeNestedNameSpecifier(NestedNameSpecifier *NNS) { |
226 | // Nested name specifiers usually aren't too long. I think that 8 would |
227 | // typically accommodate the vast majority. |
228 | SmallVector<NestedNameSpecifier *, 8> nestedNames; |
229 | |
230 | // Push each of the NNS's onto a stack for serialization in reverse order. |
231 | while (NNS) { |
232 | nestedNames.push_back(NNS); |
233 | NNS = NNS->getPrefix(); |
234 | } |
235 | |
236 | asImpl().writeUInt32(nestedNames.size()); |
237 | while (!nestedNames.empty()) { |
238 | NNS = nestedNames.pop_back_val(); |
239 | NestedNameSpecifier::SpecifierKind kind = NNS->getKind(); |
240 | asImpl().writeNestedNameSpecifierKind(kind); |
241 | switch (kind) { |
242 | case NestedNameSpecifier::Identifier: |
243 | asImpl().writeIdentifier(NNS->getAsIdentifier()); |
244 | continue; |
245 | |
246 | case NestedNameSpecifier::Namespace: |
247 | asImpl().writeNamespaceDeclRef(NNS->getAsNamespace()); |
248 | continue; |
249 | |
250 | case NestedNameSpecifier::NamespaceAlias: |
251 | asImpl().writeNamespaceAliasDeclRef(NNS->getAsNamespaceAlias()); |
252 | continue; |
253 | |
254 | case NestedNameSpecifier::TypeSpec: |
255 | case NestedNameSpecifier::TypeSpecWithTemplate: |
256 | asImpl().writeQualType(QualType(NNS->getAsType(), 0)); |
257 | continue; |
258 | |
259 | case NestedNameSpecifier::Global: |
260 | // Don't need to write an associated value. |
261 | continue; |
262 | |
263 | case NestedNameSpecifier::Super: |
264 | asImpl().writeDeclRef(NNS->getAsRecordDecl()); |
265 | continue; |
266 | } |
267 | llvm_unreachable("bad nested name specifier kind" ); |
268 | } |
269 | } |
270 | }; |
271 | |
272 | } // end namespace serialization |
273 | } // end namespace clang |
274 | |
275 | #endif |
276 | |